5 releases (stable)
1.2.2 | May 24, 2024 |
---|---|
1.2.0 | Apr 11, 2024 |
1.1.0 | Feb 29, 2024 |
1.0.0 | Feb 5, 2024 |
0.1.0 | Nov 6, 2023 |
#503 in Development tools
115KB
2K
SLoC
cargo-fixture
cargo fixture
is a cargo extension that let's you surround cargo test
with arbitrary Rust setup and teardown code.
It can be used to run network servers that tests connect to, spin up docker containers, prepare test data, check for presence of programs,... or really anything that can be done from Rust code. Any provided resources can be released after cargo test
finishes, whether manually, using RAII guards or closures.
Data can be passed from fixture to tests using environment variables, in-memory K-V storage, or files. Additionally, the option to define serial tests is provided.
Example projects:
Installation
cargo install -f cargo-fixture
Rationale
While resources can be provided using code in tests themselves, this often forces one to combine many testcases into one #[test]
or requires usage of various hacks in order to sync tests up. Custom harnesses are not well supported yet, inconvenient and face limitations (such as no output capturing).
This solution provides the same experience as regular cargo test
, testcases can be written in pretty much the same way as usual and can stay granular.
Getting started
The cargo fixture
command is used the same way as the cargo test
command with the difference that it starts a fixture before running cargo test
. The fixture continues running while cargo test
runs, it is then told about test success and is given time to perform cleanup.
cargo fixture
has the same general CLI syntax as cargo test
and except a few own flags it forwards all arguments to it.
cargo fixture
also builds code with a special feature _fixture
enabled in order to distinguish tests used with a fixture from regular ones.
This feature needs to be defined in Cargo.toml
:
[features]
_fixture = []
A fixture is a Rust program in the tests
subdirectory declared as not part of regular (libtest) test set in Cargo.toml
:
[[test]]
name = "my_fixture"
test = false
harness = false
The file is expected to contain a main()
function. The fixture and tests communicate with cargo-fixture
using clients from cargo-fixture-lib
, a Unix domain socket is used under the hood. The library uses async I/O.
A fixture looks something like this:
async fn main() {
// Connect to the cargo fixture host
let mut fixture = FixtureClient::connect().await.unwrap();
// Prepare the environment...
// Let cargo fixture know tests can be run now, wait for result
let success = fixture.ready().await.unwrap();
eprintln!("Tests finished, success: {success}");
// Wrap up.
}
A test that uses the fixture environment is then defined like this:
#[with_fixture]
#[tokio::test]
async fn some_test(client: TestClient) {
// interact with whatever fixture has prepared for us.
// client can be used to obtain K-V data.
}
The #[with_fixture]
macro marks the test #[ignore]
outside of the _fixture
feature, so that when you run plain old cargo test
, the test that require fixture are skipped! This way, you can have a hefty fixture but still run unit tests using just cargo test
quickly.
Multiple fixtures
Use cargo fixture -F <name>
to use a fixture program different than the default (fixture
).
Troubleshooting fixtures
The -x
flag lets you replace the cargo test
command with a custom one. You can use this to run a shell instead of cargo test
:
cargo fixture -x sh
This will ready the fixture and then enter a shell, in which you can inspect whether the fixture environment is prepared correctly, interact with it, or even run cargo test
and interact with the environment post-tests.
Alternatively, the shorthand cargo fixture --shell
can be used, which is equivalent to cargo fixture -x "$SHELL"
.
Platform support
OS: Linux, Mac OS, Windows 10 or later.
Dependencies
~9–19MB
~274K SLoC