Cfg test does not work with integration tests

I am trying to build a basic terminal utility. This terminal utility uses Telegram bot API under the hood and I use clap to implement this utility.

Since the application uses real HTTP server to operate, I thought it would be better to mock the HTTP. Then I have found mockito and this amazing article showing how to test terminal applications.

I have tried to glue them together. Firstly, I get the API URL as below:

// related imports
#[cfg(not(test))]
fn get_base_url<'a>() -> &'a str {
    "https://api.telegram.org/bot"
}

#[cfg(test)]
fn get_base_url<'a>() -> &'a str {
    mockito::SERVER_URL
}

As you might have guessed, the first get_base_url function is compiled into binary when it is not test while the second one is compiled in testing environment.

I do not know if relevant but here is my integration test in tests/bot.rs directory:

#[test]
fn test_successful_authentication() {
    let _m = mock("POST", "/foo/getMe")
        .with_body_from_file("tests/resources/getMe.1.json")
        .create();
    let out = process::Command::new("./target/debug/tgcli")
        .arg("bot")
        .arg("-t")
        .arg("foo")
        .output()
        .expect("failed to execute tgcli");
    let out_text = String::from_utf8_lossy(&out.stdout);
    println!("{}", out_text);
    assert!(out_text.contains("Bot authenticated successfully."))  // fails here
}

Test fails on assertion. I have put a println! over there to see what the content is. Thanks to log and fern, I have seen what URL reqwest was requesting and it was the real API URL.

Do you know why #[cfg(test)] does not compile while running integration tests?

Thanks in advance.


Environment

  • cargo 1.40.0
  • rustc 1.40.0

Dependencies

[dependencies]
clap = "2.33.0"
log = "0.4.8"
fern = "0.5.9"
chrono = "0.4.10"
reqwest = { version = "0.10.0", features = ["blocking"] }
serde = "1.0.104"
serde_json = "1.0.44"

[dev-dependencies]
mockito = "0.22.0"

Your test is running the debug build of your binary as a separate process. It should work if you call the code directly that its running in that test function rather than spawning the binary as a separate process. There may be a way to call the test build of the binary, but I'm not sure what that is.

1 Like

Even if you fixed the issue of running the non-test debug executable, it still wouldn't work. See here for more details.

1 Like

Welp, that's a shame. :frowning_face:

There is simply no way to test my CLI application in this context then. I have also tried #[cfg(debug_assertions)] in some places but that didn't qiute work either (import errors).

You could expose functions that accept cli arguments as a list of string slices, and routes that through the normal argument parsing code?

1 Like

That seems viable. I'll try that.

If you have a function that takes clap matches and does the thing, you can construct the matches from a vec instead of the command line for for tests.

https://docs.rs/clap/2.33.0/clap/struct.App.html#method.get_matches_from_safe_borrow

1 Like

So I have finally arrived at conclusion that it will be better if I abstract away the functionality of my commands with independent functions and test these exact functions.