2 releases (1 stable)
Uses new Rust 2024
new 4.0.0 | May 19, 2025 |
---|---|
0.0.0 | May 14, 2025 |
#232 in Testing
127 downloads per month
Used in srctrait-common
63KB
1K
SLoC
SrcTrait Common Testing
Structured testing with setup, teardown, and standardized fixture and temp directories
Features
- Structure tests into inheritable heirarchies: Module -> Test
- Bundle common assets outside of heirarchies using Group
- Setup and teardown callbacks for each Module, Test, and Group
- Module and Group setup occurs before Test
- Categorize based on use-case: Unit and Integration
- Standardized paths for fixture and temp directories
- Miscellaneous helper functions for common tasks
Restrictions
- One Module per Rust test module
- One Test per Rust test function
- Module and Group are static to a Rust module
- Test is instantiated non-static inside of the Rust test function
- Unit tests live in
src
- Integration tests live in
tests
orexample
- Fixture asset files live in
testing
Standard paths
use-case / module_path / function_name
Example
// Just something to test against
pub fn fibonacci(n: u8) -> Option<u128> {
if n == 0 {
return Some(0);
} else if n == 1 {
return Some(1);
} else if n > FIBONACCI_MAX_N_U128 { // overflow u128
return None;
}
let mut sum: u128 = 0;
let mut last: u128 = 0;
let mut curr: u128 = 1;
for _i in 1..n {
if let Some(r) = last.checked_add(curr) {
sum = r;
} else {
return None;
}
last = curr;
curr = sum;
}
Some(sum)
}
const FIBONACCI_MAX_N_U128: u8 = 185;
// Testing namepaths will strip '::tests' out.
#[cfg(test)]
mod tests {
use std::{fs, sync::LazyLock};
use super::*;
use srctrait_common_testing::prelude::*;
// Module setup runs before any tests that use the module.
// Teardown runs on process exit.
//
// Fixture dirs are enforced at runtime and panic if they don't exist.
//
// This module's namepath().full_path() will be:
// srctrait-common-testing/integration/example-fibonacci
//
// This module's namepath().path() will be:
// integration/example-fibonacci
//
// fixture dir: $CRATE/testing/fixtures/integration/example-fibonacci
// temp dir: $TMP/srctrait-common-testing-example-fibonacci.XXXXXXXX
static TESTING: testing::Module = testing::module!(Integration, {
.using_fixture_dir()
.using_temp_dir()
.teardown_static(teardown)
.setup(|module| {
let fibonacci_u128_csv = (0..FIBONACCI_MAX_N_U128)
.map(|n| fibonacci(n).unwrap().to_string())
.collect::<Vec<_>>()
.join(",");
let fibonacci_csv_tmp_file = module.temp_dir()
.join(&*FIBONACCI_U128_CSV_FILENAME);
fs::write(&fibonacci_csv_tmp_file, fibonacci_u128_csv).unwrap();
})
});
static FIBONACCI_U128_CSV_FILENAME: LazyLock<String> = LazyLock::new(||
"fibonacci-u128.csv".to_string());
// Anything const or static can be used with static teardown functions,
// including things like LazyLock.
//
// Module's internal teardown will delete its temp directory before
// running its custom teardown.
extern "C" fn teardown() {
println!("Farewell, Fibonacci. Don't worry, {} has already been deleted.",
*FIBONACCI_U128_CSV_FILENAME);
}
// Groups are standalone and work similar to Modules, but without children.
// They have their own tmp and fixture directories, setup, and teardown.
//
// This group's namepath().full_path() will be:
// srctrait-common-testing/integration/example-fibonacci/100
// This group's namepath().path() will be:
// integration/example-fibonacci/100
static GROUP_100: testing::Group = testing::group!("example-fibonacci/100", Integration, {
.using_fixture_dir()
});
// The #[tested] helper is merely the equivalent of #[test] and #[named]
#[tested]
fn test_u128() {
// test!() assumes a module named `TESTING` is in scope and links to it.
//
// The "inherit_" methods use the same dir as their parent module.
// The "using_" methods will append a subdir named for the test function.
//
// Tests block on their module's setup. The tmp file needed here will exist.
//
// fixture dir: $CRATE/testing/fixtures/integration/example-fibonacci/test-u128
// tmp dir: $TMP/srctrait-common-testing-example-fibonacci.XXXXXXXX
let test = testing::test!({
.using_fixture_dir()
.inherit_temp_dir()
});
let fibonacci_fixture_csv_file = test.fixture_dir().join(&*FIBONACCI_U128_CSV_FILENAME);
let expected_fibonacci_u128 = fs::read_to_string(fibonacci_fixture_csv_file).unwrap()
.split(",")
.map(|d| d.trim().parse().unwrap())
.collect::<Vec<u128>>();
let fibonacci_tmp_csv_file = test.temp_dir().join(&*FIBONACCI_U128_CSV_FILENAME);
let actual_fibonacci_u128 = fs::read_to_string(fibonacci_tmp_csv_file).unwrap()
.split(",")
.map(|i| i.parse().unwrap())
.collect::<Vec<u128>>();
assert_eq!(expected_fibonacci_u128, actual_fibonacci_u128);
}
// Demonstration using a Group
#[tested]
fn test_100() {
// This empty Test will block on its Module's setup.
let _test = testing::test!();
// Groups will block on their setup.
let fib100_file = GROUP_100.fixture_dir().join("fib-100.txt");
let fib100: u128 = fs::read_to_string(fib100_file)
.map(|d| d.trim().parse().unwrap())
.unwrap();
assert_eq!(fib100, fibonacci(100).unwrap());
}
}
Documentation
Refer to docs.rs/srctrait-common-testing
Repository
Contributors, please review SRCTRAIT.md.
Found a bug? Search for an existing issue on GitHub.
If an issue exists, chime in to add weight to it.
If not, create one and let us know.
License (AGPL3)
SrcTrait Common Testing: Structured testing in sequence with fixture and temp directories
Developed by SourceTrait
Copyright (C) 2025 Asmov LLC
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along with this program. If not, see https://www.gnu.org/licenses/.
Third-Party Licenses
crate: function_name
Our library publically exports the named macro from Daniel Henry-Mantilla's crate: function_name. It is available for use from our crate as
srctrait_common_testing::named
.
License (MIT):
Copyright (c) 2019 Daniel Henry-Mantilla
Dependencies
~3–14MB
~187K SLoC