| # Testing Rust code | |
| This document describes best practices for writing tests for Rust code. | |
| Please also refer to the [components testing guide][component-tests] 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`][example-src]. | |
| ## 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: | |
| ```rust | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/hello_world/rust/src/main.rs" region_tag="test_mod" adjust_indentation="auto" %} | |
| ``` | |
| 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. | |
| ```rust | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/hello_world/rust/src/main.rs" region_tag="async_test" adjust_indentation="auto" %} | |
| ``` | |
| 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: | |
| ```gn | |
| 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: | |
| ```gn | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/hello_world/rust/BUILD.gn" region_tag="rustc_tests" adjust_indentation="auto" %} | |
| ``` | |
| 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: | |
| ```gn | |
| 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. | |
| #### Restricting unit tests to host or Fuchsia | |
| By default, `with_unit_tests = true` defines the `<target_name>_test` target in | |
| every toolchain in which the `BUILD.gn` file is evaluated. To only define it | |
| for some platforms, set `with_unit_tests` to one of the following strings, | |
| which match the interface of the Bazel Rust macros: | |
| * `"host"`: only define the test target in host toolchains. | |
| * `"fuchsia"`: only define the test target in Fuchsia toolchains. | |
| * `"both"`: define the test target in both host and Fuchsia toolchains. | |
| For example, to only build and run the unit tests of a library on the host: | |
| ```gn | |
| rustc_library("lib") { | |
| name = "my_lib" | |
| edition = "2024" | |
| with_unit_tests = "host" | |
| sources = [ "src/lib.rs" ] | |
| } | |
| group("tests") { | |
| testonly = true | |
| deps = [ ":lib_test($host_toolchain)" ] | |
| } | |
| ``` | |
| Note: `rustc_macro` only accepts `"host"`, and `rustc_dylib` only accepts | |
| `"fuchsia"`, in addition to boolean values. | |
| ### 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][glossary.component-manifest] that declares the capabilities | |
| required by your tests. | |
| Your `BUILD.gn` file needs to import the component templates: | |
| ```gn | |
| import("//build/components.gni") | |
| ``` | |
| 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). | |
| ```gn | |
| {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/hello_world/rust/BUILD.gn" region_tag="fuchsia_test" adjust_indentation="auto" %} | |
| ``` | |
| To run the tests run: | |
| ```posix-terminal | |
| fx test hello-world-rust-tests | |
| ``` | |
| For information on packaging and running tests, see | |
| [test components][component-tests] and [building components][build-tests]. | |
| ### 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`. | |
| ```gn | |
| rustc_binary("bin") { | |
| name = "my_test" | |
| with_unit_tests = true | |
| edition = "2024" | |
| test_deps = [ | |
| "//third_party/rust_crates:matches", | |
| "//third_party/rust_crates:pretty_assertions", | |
| ] | |
| } | |
| ``` | |
| [build-tests]:/docs/development/components/build.md#test-packages | |
| [component-tests]:/docs/development/testing/components/test_component.md | |
| [example-src]: /examples/hello_world/rust | |
| [glossary.component-manifest]: /docs/glossary/README.md#component-manifest | |
| [`assert_matches`]: https://fuchsia-docs.firebaseapp.com/rust/assert_matches/index.html | |
| [`pretty_assertions`]: https://fuchsia-docs.firebaseapp.com/rust/pretty_assertions/index.html |