defmt-test
defmt-test is a test harness for embedded devices that lets you write and run unit tests on your device as if you were using the built-in #[test] attribute.
It is compatible with rust-analyzer's ▶ Run Test button, which means you can flash and run your tests straight from VS Code:

For a full list of defmt-test's capabilities, please refer to the documentation below.
Basic usage
- If you don't have a project setup yet, start from the
app-template, then proceed with step 3. If you have a project based on an old version ofapp-templatethen proceed with step 1.
- In the testsuite crate, add
defmt-testto the dependencies:
# testsuite/Cargo.toml
[dependencies]
+ defmt-test = "0.1.0"
- Within the
testsuite/tests/test.rsfile, create atestsmodule and mark it with the#[defmt_test::tests]attribute. Within that module writestd-like unit tests: functions marked with the#[test]attribute.
// testsuite/tests/test.rs
- Run
cargo test -p testsuiteto run the unit tests
$ cargo test -p testsuite
(..)
0.000000 INFO (1/2) running `assert_true`...
└─ test::tests::__defmt_test_entry @ tests/test.rs:7
0.000001 INFO (2/2) running `assert_false`...
└─ test::tests::__defmt_test_entry @ tests/test.rs:7
0.000002 ERROR panicked at 'TODO: write actual tests', testsuite/tests/test.rs:16:9
└─ panic_probe::print_defmt::print @ (..omitted..)
stack backtrace:
0: HardFaultTrampoline
<exception entry>
1: __udf
2: cortex_m::asm::udf
at (..omitted..)
3: rust_begin_unwind
at (..omitted..)
4: core::panicking::panic_fmt
at (..omitted..)
5: core::panicking::panic
at (..omitted..)
6: test::tests::assert_false
at tests/test.rs:16
7: main
at tests/test.rs:7
8: ResetTrampoline
at (..omitted..)
9: Reset
at (..omitted..)
NOTE unit tests will be executed sequentially
Adding state
An #[init] function can be written within the #[tests] module.
This function will be executed before all unit tests and its return value, the test suite state, can be passed to unit tests as an argument.
// state shared across unit tests
$ cargo test -p testsuite
0.000000 INFO (1/2) running `assert_true`...
└─ test::tests::__defmt_test_entry @ tests/test.rs:11
0.000001 INFO (2/2) running `assert_flag`...
└─ test::tests::__defmt_test_entry @ tests/test.rs:11
0.000002 INFO all tests passed!
└─ test::tests::__defmt_test_entry @ tests/test.rs:11
Test Outcome
Test functions may either return () and panic on failure, or return any other type that implements the TestOutcome trait, such as Result.
This allows tests to indicate failure via Result, which allows using the ? operator to propagate errors.
Similar to Rust's built-in #[should_panic] attribute, defmt-test supports a #[should_error] attribute, which inverts the meaning of the returned TestOutcome.
Err makes the test pass, while Ok/() make it fail.
Support
defmt-test is part of the Knurling project, Ferrous Systems' effort at
improving tooling used to develop for embedded systems.
If you think that our work is useful, consider sponsoring it via GitHub Sponsors.
License
Licensed under either of
-
Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
-
MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be licensed as above, without any additional terms or conditions.