Home > Mobile >  How to integrate test-cases specified by external files into `cargo test`
How to integrate test-cases specified by external files into `cargo test`

Time:11-09

Within Rusts build-tool cargo, I can define a function for a test-case simply this way:

#[test]
fn test_something() {
    assert_eq!(true, true);
}

Running with cargo test outputs:

Running unittests src/lib.rs (target/debug/deps/example-14178b7a1a1c9c8c)

running 1 test
test test::test_something ... ok

test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

So far, so good. Now I have the special case, that test cases in my project are defined in files. They can be executed using a special testcase() function that performs all steps to run the test, and do specific assert_eq!() calls.

Now I want to achieve the following:

  1. Read all files from a tests/ folder
  2. Run testcase() on every file
  3. Have every file as a single test case in cargo test

Latter one is the problem. This is the function (as test) to run all tests from the folder using the testcase()-function.

#[test]
// Run all tests in the tests/-folder
fn tests() {
    let cases = std::fs::read_dir("tests").unwrap();

    for case in cases {
        testcase(case.unwrap().path().to_str().unwrap());
    }
}

A run of cargo test should print the following, given the testcases a.test, b.test and c.test:

Running unittests src/lib.rs (target/debug/deps/example-14178b7a1a1c9c8c)

running 3 test
test test::tests::tests/a.test ... ok
test test::tests::tests/b.test ... ok
test test::tests::tests/c.test ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s

Is this somehow possible?

CodePudding user response:

With Rust's built-in test harness, there is no way to declare a single test case other than with the #[test] attribute. In order to get the result you want, you would need to write a proc-macro which reads your directory of tests and generates a #[test] function for each one.

Instead, it will probably be more practical to write a test which uses your own harness code. In your Cargo.toml, define a test target with it disabled:

[[test]]
name = "lang_tests"
harness = false

Then in tests/lang_tests.rs (the tests/ directory goes next to src/ and is already known to Cargo), write a program with an ordinary main() that runs tests:

fn main() {
    let cases = std::fs::read_dir("tests").unwrap();

    let mut passed = 0;
    let mut failed = 0;
    for case in cases {
        if testcase(case.unwrap().path().to_str().unwrap()) {
            eprintln!("...");
            passed  = 1;
        } else {
            eprintln!("...");
            failed  = 1;
        }
    }

    eprintln!("{passed} passed, {failed} failed");
    if failed > 0 {
        std::process::exit(1);
    }
}

Of course there's a bunch of further details to implement here — but I hope this illustrates how to write a custom test harness that does the thing you want to do. Again, you cannot do it with the default test harness except by using macro-generated code, which is probably not worthwhile in this case.

  • Related