Introduction
(deleted)
Some people want to set the result of a test case fail but still, run full of the test as mentioned here.
If Rust can provide #[test]
on the module, we can easier run all the functions in that module, let the module become a test case with a set of functions that need to be tested, and also have only one test result of the module. If some tests fail, the other tests still run. Following is the thing I want to propose.
Proposal
Let #[test]
apply to the module, and the module will be a single integrated test case, all the functions in the module will run, if any function fails, the test case fails. If more than one functions fail, the test case fails, and also all the error message are collected inside the test case, and still easy to debug on it, and the summary will keep as clean as possible.
#[cfg(test)]
#[test]
mod infra {
fn infra_1_works () {...}
fn infra_2_works () {...}
}
#[test]
fn it_works() {...}
running 2 tests
test infra ... ok
test it_works ... ok
test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out;
finished in 0.00s
If there is one error in there, it will probably be like this.
#[cfg(test)]
#[test]
mod infra {
fn infra_1_works () {...}
fn infra_2_fails () {assert!(fail)}
}
#[test]
fn it_works() {...}
running 2 tests
test infra ... FAILED
test it_works ... ok
failures:
---- infra stdout ----
thread 'infra::tinfra_2_fails' panicked at 'assertion failed: false', src/lib.rs:0:0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
infra_test
test result: FAILED. 1 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out;
finished in 0.00s
If there are more error in there, it will probably be like this.
#[cfg(test)]
#[test]
mod infra {
fn infra_1_fails () {assert!(fail)}
fn infra_2_fails () {assert!(fail)}
}
#[test]
fn it_fails() {assert!(fail)}
running 2 tests
test infra ... FAILED
test it_fails ... FAILED
failures:
---- infra_test stdout ----
thread 'infra::infra_1_fails' panicked at 'assertion failed: false', src/lib.rs:0:0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread 'infra::infra_2_fails' panicked at 'assertion failed: false', src/lib.rs:0:0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
---- it_fail stdout ----
thread 'it_fails' panicked at 'assertion failed: false', src/lib.rs:0:0
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
infra
it_fails
test result: FAILED. 0 passed; 0 failed; 2 ignored; 0 measured; 0 filtered out;
finished in 0.00s
As you can see, all the functions in the #[test]
module will run and the error message will be collected in the test case of #[test]
module. The #[test]
module is just a test case, and collect all the error message from each function.
Functions Inside the #[test]
mod
The #[test]
mod is a test case that is a set of the test functions and only all the test functions that pass the test case of the mod will be passed. All the test functions in the #[test]
mod run dependently. By nature, we use the null input function as a test. and some helper functions with inputs, we do not need to tell whether the function is a helper or not.
If there is a useful function without inputs that should not be run in a test mod, we could use #[test(not)]
to exclude it. We also discussed this in Zulip. Currently, the RFC does not propose #[test(not)]
, because this kind function should not be in a test mod.
The functions in #[test]
mod
do not guarantee to run in any form, the way to run these functions is the same as #[test]
on fn
. The #[test]
on mod
changes the test counting and summary on cargo test
. The proposal does not change the way running the test functions, and the functions inside the #[test]
mod
are parts of a test case. This means the test filter works on #[test]
mod
but not any functions inside the mod. Because I think this may not be useful but has a higher complexity for implementation, If you highly recommended doing a filter please help me to refine this.
Nesting issues
Currently, #[test]
on mod let a set of functions become a test. The proposal do not addressing nesting mod issues, the similar is not addressing with #[test]
on functions.
#[test]
fn it_works() {
#[test]
fn it_fails() {
assert!(false)
}
}
running 1 test
test it_works ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
The behavior of nesting #[test]
mod will be the same, only the first level no input function will become a set to be a test case.
Other test-related attributes
A test-related attribute still works on the test function, the #[test]
on the mod is building a test case with a set of the test functions. Following is the example to work woth #[should_panic]
#[cfg(test)]
#[test]
mod infra {
fn it_works () {..}
#[should_panic]
fn it_panic () {panic!("...")}
}
Currently, the proposal does not raise the test-related attribute on the mod level. If you highly recommended doing this because I think this may not be easy to read, please help me to refine this.
What are the benefits of this design
-
keep the summary of
cargo test
clean and show the important thingsWhen the test cases become more and more, we can remove the
#[test]
of functions, and put#[test]
in the mod. -
Find issues with running
cargo test
onceFor the original purpose, we can test everything in functions, and make sure they will be tested, even one of the function fails.