Testing Rust code

This document describes best practices for writing tests for Rust code. Please also refer to the components testing guide for instructions on defining test packages and components and running them.

This document is targeted towards developers working inside of fuchsia.git, and the workflow described is unlikely to work for IDK consumers.

The source code for this tutorial is available at //examples/hello_world/rust.

Unit tests

Adding tests to code

The idiomatic way for adding Rust unit tests works just as well inside of Fuchsia as it does outside, and can be easily accomplished by dropping the following snippet into the bottom of whatever test you want to write:

#[cfg(test)]
mod tests {
    #[fuchsia::test]
    fn it_works() {
        assert_eq!(true, true);
    }
}

This will cause a new mod named tests to be created, and this mod will only be included when building unit tests. Any functions annotated with #[fuchsia::test] will be run as a test, and if the function successfully returns then the test passes.

#[fuchsia::test] also supports tests exercising asynchronous code.

#[fuchsia::test]
async fn my_test() {
    let some_future = async { 4 };
    assert_eq!(some_future.await, 4);
}

The typical #[test] annotation also works, but it doesn't support async tests or logging out of the box. Prefer #[test] in situations where you think the crate might get used outside of the Fuchsia codebase.

Building tests

The unit tests can be automatically built by Rust targets (i.e. either rustc_binary or rustc_library). The approaches are by and large similar.

Building tests for a Rust binary

This section is useful if you are testing a rust binary (i.e. you have a main.rs). If you have a library instead, see the next section.

Your BUILD.gn file first needs to make available the rustc_binary template by importing it:

import("//build/rust/rustc_binary.gni")

Unit tests are built by the rustc_binary GN template only if the setting with_unit_tests = true is added:

rustc_binary("bin") {
  name = "hello_world_rust"

  # Generates the "bin_test" build target
  with_unit_tests = true
  edition = "2021"

  deps = []
  test_deps = [ "//src/lib/fuchsia" ]

  sources = [ "src/main.rs" ]
}

Setting with_unit_tests = true causes this build rule to generate two different executables, one with the provided and one with _bin_test appended to the provided name.

In our example here, the executable names that are created are called:

  • hello_world_rust; and
  • hello_world_rust_bin_test.

Building tests for a Rust library

Your BUILD.gn file first needs to make available the rustc_library template by importing it:

import("//build/rust/rustc_library.gni")

Unit tests are built by the rustc_library GN template only if the setting with_unit_tests = true is added, similarly to how it is done in the case of rustc_binary above.

In this case, however, a differently named test binary is created:

  • hello_world_rust_lib_test. Note that the name of the binary is different from the name generated by the library.

The binary names are important because they will be used in followup steps.

Packaging and running tests

To run the tests that were generated by previous targets, package them as test components. Test components contain a component manifest that declares the capabilities required by your tests. You can package tests using the following build rules:

  • fuchsia_test_package(): Package template that collects multiple test components and their dependencies together in a single package. Test packages are typically used for integration tests.
  • fuchsia_unittest_package(): Specialized template for packages containing unit tests. Unit test packages can generate a minimal component manifest for unit tests with no required capabilities.

For the Hello World binary example, the unit test package references the generated targets, bin_test (based on target name bin and the implicit suffix _test), and hello_world_rust_bin_test (based on the value of name stanza).

fuchsia_unittest_package("hello-world-rust-tests") {
  deps = [ ":bin_test" ]
}

To run the tests run:

fx test hello-world-rust-tests

For information on packaging and running tests, see test components and building components.

Helpful crates

The following in-tree third-party crates can help you write tests:

  • assert_matches: provides the macro assert_matches!, making pattern assertions ergonomic.
  • pretty_assertions: provides an alternative assert_eq! macro that displays a colored diff when the assertion fails.

These can be included in your BUILD.gn under test_deps.

rustc_binary("bin") {
  name = "my_test"
  with_unit_tests = true
  edition = "2021"

  test_deps = [
    "//third_party/rust_crates:matches",
    "//third_party/rust_crates:pretty_assertions",
  ]
}