How to initialize the logger for integration tests?
Asked Answered
F

4

46

I have a crate with production code in the src directory and integration tests in the tests directory. The production code uses log macros.

I would like to init a global logger when running the integration tests (e.g. env_logger::init().unwrap();) There are several tests and the test order is not defined, so I don't know in which test I should put the initialize command.

Is there any way I can do this nicely? Perhaps by overriding the tests main function?

Fortyfive answered 11/5, 2015 at 21:19 Comment(0)
S
46

You can use something like this:

use std::sync::Once;

static INIT: Once = Once::new();

/// Setup function that is only run once, even if called multiple times.
fn setup() {
    INIT.call_once(|| {
        env_logger::init().unwrap();
    });
}

Then simply call setup() in the beginning of each test.

Originally based on this blogpost.

Sanguinary answered 29/3, 2017 at 12:21 Comment(3)
Could anyone provide a full example? Where this code should go?Favorable
@Favorable Just copy this code in your test file, then call setup() in each test. Beware, the env_logger::init() no longer returns a Result, hence the unwrap should be removed (see try_init for a Result).Brouhaha
This technique also works to initialize a simple_logger instance by substituting env_logger::init().unwrap() with simple_logger::init_with_env().unwrap().Literator
K
14

The latest documentation has a recommendation on Capturing logs in tests :: env_logger:

Records logged during cargo test will not be captured by the test harness by default. The Builder::is_test method can be used in unit tests to ensure logs will be captured:

#[cfg(test)]
mod tests {
    fn init() {
        let _ = env_logger::builder().is_test(true).try_init();
    }
 
    #[test]
    fn it_works() {
        init();
        info!("This record will be captured by `cargo test`");
 
        assert_eq!(3, 1 + 2);
    }
}
Kayser answered 20/3, 2019 at 18:56 Comment(4)
I don't know what the documentation is attempting to show, but the init function is never called by anything and the info! line is not printed when the test either passes or fails (via RUST_LOG=info cargo test).Service
The init function has to be called explicitly, it's fixed in docs for v0.6.2. Also, log printing happens only if the test fails (which i think is good).Trundle
I'm getting panicked at 'env_logger::init should not be called after logger initialized: SetLoggerError(())',Favorable
This looked promising but had no effect on my integration test (github.com/near/workspaces-rs). Also, the latest link I think is docs.rs/env_logger/0.9.0/env_logger/….Montano
Q
7

In addition to Danilo Bargen's comment, you can write it in a shorter form:

use std::sync::Once;

static INIT: Once = Once::new();

fn setup() {
  INIT.call_once(env_logger::init);
}
Quentinquercetin answered 4/6, 2020 at 8:4 Comment(0)
C
6

For now, you can just re-initialize the logger at the top of every test and ignore the error. It's not a pretty solution but it works and is perfectly safe.

let _ = env_logger::try_init();

// your test code...
Conjugal answered 17/8, 2016 at 22:22 Comment(2)
This no longer works, you get an panic when calling init multiple times: panicked at 'env_logger::init should not be called after logger initialized: SetLoggerError(())Acuminate
Maybe try_init?Winson

© 2022 - 2024 — McMap. All rights reserved.