9 个版本

0.3.2 2024年3月5日
0.3.1 2023年10月11日
0.3.0 2021年11月10日
0.2.3 2021年5月21日
0.1.0 2020年11月16日

#625 in 嵌入式开发

Download history 267/week @ 2024-03-13 286/week @ 2024-03-20 166/week @ 2024-03-27 252/week @ 2024-04-03 257/week @ 2024-04-10 306/week @ 2024-04-17 224/week @ 2024-04-24 195/week @ 2024-05-01 141/week @ 2024-05-08 254/week @ 2024-05-15 364/week @ 2024-05-22 202/week @ 2024-05-29 215/week @ 2024-06-05 280/week @ 2024-06-12 275/week @ 2024-06-19 428/week @ 2024-06-26

1,232 每月下载量
用于 7 crates

MIT/Apache

410KB
1.5K SLoC

defmt-test

defmt-test 是一个嵌入式设备的测试平台,允许您像使用内置的 #[test] 属性一样在您的设备上编写和运行单元测试。

它与 rust-analyzer▶ 运行测试 按钮兼容,这意味着您可以直接从 VS Code 中烧录和运行测试。

demo: clicking the run button above a defmt_test::tests module leads to flashing & test run

有关 defmt-test 功能的完整列表,请参阅以下文档。

在新的项目中使用 defmt-test

我们建议您从 app-template 开始。从这里,您可以执行 cargo test --lib 以运行库单元测试,即库 crate (src/lib.rs) 中的 #[test] 函数。

$ cargo test --lib
(..)
(1/1) running `it_works`...
└─ app::unit_tests::__defmt_test_entry @ src/lib.rs:33
all tests passed!
└─ app::unit_tests::__defmt_test_entry @ src/lib.rs:28
(..)
(HOST) INFO  device halted without error

执行 cargo test --test integration 以运行集成测试,即 tests/integration.rs 文件。

$ cargo test --test integration
(..)
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..)

注意:文件中所有 #[test] 函数都将依次运行。

defmt-test 添加到现有项目

如果您想将 defmt-test 添加到现有的 Cargo 项目/包中,对于您想要测试的每个 crate,您需要在 Cargo.toml 中进行以下更改

  • defmt-test 添加为 dev-dependency
  • 对于您想要测试的每个 crate,将 harness 设置为 false 以禁用默认测试 harness,即依赖于 stdtest crate。以下是一些示例
# Cargo.toml

# for the library crate (src/lib.rs)
[lib]
harness = false

# for each crate in the `tests` directory
[[test]]
name = "test-name" # tests/test-name.rs
harness = false

[[test]]
name = "second" # tests/second.rs
harness = false

另外需要注意的一点是,cargo test 将编译包或 workspace 中的所有 crate。这可能包括您不想测试的 crate,如 src/main.rssrc/binexamples 中的每个 crate。要识别 cargo test 编译了哪些 crate,请运行 cargo test -j1 -v 并查找每个 rustc 调用传递的 --crate-name 标志。

要仅测试包/工作区中的一部分 crate,您有两种选择

  • 您可以在调用 cargo test 时指定每个 crate。例如,cargo test --lib --test integration 测试了两个 crate:库 crate(《code>src/lib.rs)和 tests/integration.rs
  • 您还可以禁用不需要测试的 crate 的测试 -- 以下是一个示例 -- 然后您可以使用 cargo test 来测试所有未禁用的 crate。

如果您有这种项目结构

$ tree .
.
├── Cargo.toml
├── src
  ├── lib.rs
  └── main.rs
└── tests
   └── integration.rs

并且已经为 src/lib.rs 设置了测试,但不希望测试 src/main.rs,您需要禁用 src/main.rs 的测试

# Cargo.toml
[package]
# ..
name = "app"

[[bin]] # <- add this section
name = "app" # src/main.rs
test = false

添加状态

可以在 #[tests] 模块中编写一个 #[init] 函数。这个函数将在所有单元测试之前执行,它的返回值,即测试套件 状态,可以作为参数传递给单元测试。

// state shared across unit tests
struct MyState {
    flag: bool,
}

#[defmt_test::tests]
mod tests {
    #[init]
    fn init() -> super::MyState {
        // state initial value
        super::MyState {
            flag: true,
        }
    }

    // This function is called before each test case.
    // It accesses the state created in `init`,
    // though like with `test`, state access is optional.
    #[before_each]
    fn before_each(state: &mut super::MyState) {
        defmt::println!("State flag before is {}", state.flag);
    }

    // This function is called after each test
    #[after_each]
    fn after_each(state: &mut super::MyState) {
        defmt::println!("State flag after is {}", state.flag);
    }

    // this unit test doesn't access the state
    #[test]
    fn assert_true() {
        assert!(true);
    }

    // but this test does
    #[test]
    fn assert_flag(state: &mut super::MyState) {
        assert!(state.flag)
        state.flag = false;
    }
}
$ cargo test -p testsuite
0.000000 (1/2) running `assert_true`...
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:37
0.000001 State flag before is true
└─ integration::tests::before_each @ tests/integration.rs:26
0.000002 State flag after is true
└─ integration::tests::after_each @ tests/integration.rs:32
0.000003 (2/2) running `assert_flag`...
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:43
0.000004 State flag before is true
└─ integration::tests::before_each @ tests/integration.rs:26
0.000005 State flag after is false
└─ integration::tests::after_each @ tests/integration.rs:32
0.000006 all tests passed!
└─ integration::tests::__defmt_test_entry @ tests/integration.rs:11

测试结果

测试函数可以返回 () 并在失败时 panic,或者返回实现 TestOutcome 特性的任何其他类型,例如 Result

这允许测试通过 Result 来指示失败,这允许使用 ? 运算符来传播错误。

类似于 Rust 内置的 #[should_panic] 属性,defmt-test 支持一个 #[should_error] 属性,它反转了返回的 TestOutcome 的含义。当返回 Err 时,测试通过,而当返回 Ok/() 时,测试失败。

支持

defmt-testKnurling 项目的一部分,这是 Ferrous Systems 为提高用于嵌入式系统开发的工具而进行的努力。

如果您认为我们的工作很有用,请考虑通过 GitHub Sponsors 赞助它。

许可协议

根据您的选择,许可协议可以是以下之一:

贡献

除非您明确声明,否则根据 Apache-2.0 许可证的定义,您有意提交以包含在作品中的任何贡献都将按照上述许可证进行许可,不附加任何额外条款或条件。

依赖

~2.5MB
~46K SLoC