[−][src]Crate expect_test
Minimalistic snapshot testing for Rust.
Introduction
expect_test
is a small addition over plain assert_eq!
testing approach,
which allows to automatically update tests results.
The core of the library is the expect!
macro. It can be though of as a
super-charged string literal, which can update itself.
Let's see an example:
use expect_test::expect; let actual = 2 + 2; let expected = expect![["5"]]; expected.assert_eq(&actual.to_string())
Running this code will produce a test failure, as "5"
is indeed not equal
to "4"
. Running the test with UPDATE_EXPECT=1
env variable however would
"magically" update the code to:
let actual = 2 + 2; let expected = expect![["4"]]; expected.assert_eq(&actual.to_string())
This becomes very useful when you have a lot of tests with verbose and potentially changing expected output.
Under the hood, expect!
macro uses file!
and line!
to record source
position at compile time. At runtime, this position is used to patch the
file in-place, if UPDATE_EXPECT
is set.
Guide
expect!
returns an instance of Expect
struct, which holds position
information and a string literal. Use Expect::assert_eq
for string
comparison. Use Expect::assert_debug_eq
for verbose debug comparison. Note that
leading indentation is automatically removed.
use expect_test::expect; #[derive(Debug)] struct Foo { value: i32, } let actual = Foo { value: 92 }; let expected = expect![[" Foo { value: 92, } "]]; expected.assert_debug_eq(&actual);
Be careful with assert_debug_eq
- in general, stability of the debug
representation is not guaranteed. However, even if it changes, you can
quickly update all the tests by running the test suite with UPDATE_EXPECT
environmental variable set.
If the expected data is too verbose to include inline, you can store it in an
external file using the expect_file!
macro:
use expect_test::expect_file; let actual = 42; let expected = expect_file!["./the-answer.txt"]; expected.assert_eq(&actual.to_string());
File path is relative to the current file.
Suggested Workflows
I like to use data-driven tests with expect_test
. I usually define a single
driver function check
and then call it from individual tests:
use expect_test::{expect, Expect}; fn check(actual: i32, expect: Expect) { let actual = actual.to_string(); expect.assert_eq(&actual); } #[test] fn test_addition() { check(90 + 2, expect![["92"]]); } #[test] fn test_multiplication() { check(46 * 2, expect![["92"]]); }
Each test's body is a single call to check
. All the variation in tests
comes from the input data.
When writing a new test, I usually copy-paste an old one, leave the expect
blank and use UPDATE_EXPECT
to fill the value for me:
#[test] fn test_division() { check(92 / 2, expect![[]]) }
See https://blog.janestreet.com/using-ascii-waveforms-to-test-hardware-designs/ for a cool example of snapshot testing in the wild!
Alternatives
- insta - a more feature full snapshot testing library.
- k9 - a testing library which includes support for snapshot testing among other things.
Maintenance status
The main customer of this library is rust-analyzer. The library is expected to be relatively stable, but, if the need arises, it could be significantly reworked to fit rust-analyzer better.
MSRV: latest stable.
Macros
expect | Creates an instance of |
expect_file | Creates an instance of |
Structs
Expect | Self-updating string literal. |
ExpectFile | Self-updating file. |
Position | Position of original |