Log and tracing initialization in crate tests

I have a crate that initializes env_logger and tracing-subscriber along a path that I need to run in tests. The tests panic because the env_logger is initialized twice.

I know I can get past the immediate issue by using try_init() instead. However, due to the nature of the crate it makes sense to use init() for non-tests.

Is there a way to get the crate to implicitly call try_init() for test builds and init() for non-test builds?

Just to be clear: This isn't a question about logging in tests (which is what most google hits seem to be about) -- I don't actually need logging and tracing in these tests, but their initializations are part of a runtime initialization that is needed for the tests.

#[cfg(not(test))]
fn env_logger_init() { env_logger::init(); }

#[cfg(test)]
fn env_logger_init() { let _ = env_logger::try_init(); }

Just to be clear: The initialization lives in the library crate, not the test. The #[cfg(test)] does not affect the actual library crate, just the executable tests driver executable.

applications that pretend to be a libraries are not easily tested. basically, they rename main.rs to lib.rs and fn main() to pub fn init(), and call it a "library". I don't like to call them real libraries, because they are not reusable. (for example, I have encountered crates which don't expose data types but only a small number of "entry" functions, and they return a black box error type that can only be formatted to string).

one workaround is to include only one test case per test file, this way you won't call the initialization twice, but your tests are scattered all over the place.

1 Like