Testing async code
Standard unit tests (#[test]) cannot test async code. To test async code, you need an async runtime like tokio or async-std.
Using tokio
[dev-dependencies]
tokio = { version = "1", features = ["macros", "rt"] }
Use #[tokio::test] instead of #[test].
Using async-std
[dev-dependencies]
async-std = { version = "1", features = ["attributes"] }
Use #[async_std::test] instead of #[test].
Mocking dependencies
Use mockall crate https://crates.io/crates/mockall.
use mockall::automock;
#[automock]
pub trait Database {
async fn get_user(&self, id: u32) -> u32;
}
automock gives you a mock implementation with the prefix Mock (in the above example, it is MockDatabase). It generates methods on the mock that can be used to setup expectations and verify the expectations
#[tokio::test]
async fn test_with_mock() {
let mut mock = MockDatabase::new();
// Setup expectations
mock.expect_get_user()
.with(mockall::predicate::eq(5))
.returning(|_| 42); // Returns a value, not a future!
// Actual call that exercises the mock
let result = mock.get_user(5).await;
assert_eq!(result, 42);
}
Mocking HTTP calls
Mocking reqwest is difficult due to the fact that you cannot create the reqwest::Response or reqwest::Client objects. There are two options
- Mock the network server by starting a fake local server if you care about the HTTP details.
- Mock the logic (hide the reqwest behind a trait) if you don’t care about the HTTP details.
- Do not return
reqwest::Responsein your trait. Return your domain objects likeUser(e.g.,Result<User, Error>)
- Do not return
Use wiremock to spin up a local server.
Hide the dependency logic behind a trait and mock the trait for tests using mockall.
Code coverage
Generate code coverage data by using cargo tarpaulin https://crates.io/crates/cargo-tarpaulin
# This command takes about 2m
cargo install cargo-tarpaulin
# On-demand
cargo tarpaulin --out Lcov
# Auto-generation
cargo watch -x "tarpaulin --out Lcov"
View the coverage data in vscode using Coverage Gutters by ryanluker. Coverage Gutters requires an LCOV file.
Show coverage by using the command >Coverage Gutters: Display Coverage.
Code coverage using nextest and cargo-llvm-cov
cargo install cargo-nextest --locked
cargo +stable install cargo-llvm-cov --locked
https://nattrio.medium.com/visualizing-rust-code-coverage-in-vs-code-781aaf334f11
Generate the coverage report
cargo llvm-cov nextest
# Generate coverage report in lcov format for vscode
cargo llvm-cov nextest --lcov --output-path ./target/lcov.info
cargo watch -x 'llvm-cov nextest --lcov --output-path ./target/lcov.info' -w src
Open the report in HTML format
cargo llvm-cov nextest --open