2 releases
Uses new Rust 2024
new 0.1.1 | Apr 21, 2025 |
---|---|
0.1.0 | Apr 20, 2025 |
#2070 in Rust patterns
76 downloads per month
14KB
212 lines
Subprocess test
Introduces macro and some infrastructure that allows running your test code in separate subprocess and verify it, if needed, inside test invocation, like normal. See crate documentation for details.
Some small examples:
subprocess_test::subprocess_test! {
#[test]
fn just_success() {
let value = 1;
assert_eq!(value + 1, 2);
}
}
subprocess_test::subprocess_test! {
#[test]
fn one_plus_one() {
println!("{}", 1 + 1);
}
verify |success, output| {
assert!(success);
assert_eq!(output, "2\n");
}
}
lib.rs
:
This crate exposes single utility macro subprocess_test
Macro generates test function code in such a way that first test code block is executed in separate subprocess by re-invoking current test executable, its output is captured, filtered a bit and then fed to verification function. Test decides whether it's in normal or subprocess mode through marker environment variable
Used when one needs to either run some test in isolation or validate test output regardless of its proper completion, i.e. even if it aborts
Small examples
subprocess_test::subprocess_test! {
#[test]
fn just_success() {
let value = 1;
assert_eq!(value + 1, 2);
}
}
subprocess_test::subprocess_test! {
#[test]
fn one_plus_one() {
println!("{}", 1 + 1);
}
verify |success, output| {
assert!(success);
assert_eq!(output, "2\n");
}
}
Usage
subprocess_test::subprocess_test! {
// Mandatory test marker attribute; psrens are needed
// only if some attribute parameters are specified.
//
// Please also note that this attribute must be first,
// and its optional parameters must maintain order.
// This is due to limitations of Rust's macro-by-example.
#[test(
// Optionally specify name of environment variable used to mark subprocess mode.
// Default name is "__TEST_RUN_SUBPROCESS__", so in very unprobable case case
// you're getting name collision here, you can change it.
env_var_name = "RUN_SUBPROCESS_ENV_VAR",
// While subprocess is executed using `cargo test -q -- --nocapture`,
// there's still some output from test harness.
// To filter it out, test prints two boundary lines, in the beginning
// and in the end of test's output, regardless if it succeeds or panics.
// The default boundary line is "========================================",
// so in rare case you expect conflict with actual test output, you can use
// this parameter to set custom output boundary.
output_boundary = "<><><><><><><><>",
)]
// Any other attributes are allowed, yet are optional
#[ignore]
// Test can have any valid name, same as normal test function
fn dummy() {
// This block is intended to generate test output,
// although it can be used as normal test body
println!("Foo");
eprintln!("Bar");
}
// `verify` block is optional;
// if absent, it's substituted with block which just asserts that subprocess succeeded
// and prints test output in case of failure
//
// Parameters can be any names. Their meanings:
// * `success` - boolean which is `true` if subprocess succeeded
// * `output` - subprocess output collected into string, both `stdout` and `stderr`
verify |success, output| {
// This block is run as normal part of test and in general must succeed
assert!(success);
assert_eq!(output, "Foo\nBar\n");
}
}
Limitations
Macro doesn't work well with #[should_panic]
attribute because there's only one test function
which runs in two modes. If subprocess test panics as expected, subprocess succeeds, and
verify
block must panic too. Just use verify
block and do any checks you need there.
Dependencies
~2–11MB
~130K SLoC