assert_or_bless/
lib.rs

1/// Asserts that `actual` matches the text-file snapshot at `snapshot_path`. If
2/// there is a diff the function will panic with a helpful diff that shows what
3/// changed.
4///
5/// If the env var `ASSERT_OR_BLESS` is set to `bless` then `actual` will be
6/// written to the snapshot file at `snapshot_path` instead of asserting that it
7/// matches.
8pub fn assert_eq_or_bless(actual: impl AsRef<str>, snapshot_path: impl AsRef<std::path::Path>) {
9    assert_eq_or_bless_if(
10        actual,
11        snapshot_path,
12        std::env::var("ASSERT_OR_BLESS") == Ok("bless".to_string()),
13    );
14}
15
16/// Same as [`assert_eq_or_bless`] but allows you to use custom logic to
17/// determine when to bless. Maybe you want to use a different environment
18/// variable, for example.
19pub fn assert_eq_or_bless_if(
20    actual: impl AsRef<str>,
21    snapshot_path: impl AsRef<std::path::Path>,
22    bless: bool,
23) {
24    let snapshot_path = snapshot_path.as_ref();
25    let actual = actual.as_ref();
26
27    if bless {
28        // Write the current public API to the snapshot path
29        std::fs::write(snapshot_path, actual)
30            .unwrap_or_else(|err| panic!("Writing `{snapshot_path:?}`: {err}"));
31    } else {
32        // Assert that the current public API matches the snapshot
33        let expected = std::fs::read_to_string(snapshot_path)
34            .unwrap_or_else(|err| panic!("Reading `{snapshot_path:?}`: {err}"));
35        similar_asserts::assert_eq!(actual, expected);
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    struct DirGuard(std::path::PathBuf);
44    impl DirGuard {
45        fn new(path: std::path::PathBuf) -> Self {
46            std::fs::create_dir_all(&path).unwrap();
47            Self(path)
48        }
49    }
50    impl Drop for DirGuard {
51        fn drop(&mut self) {
52            let _ = std::fs::remove_dir_all(&self.0);
53        }
54    }
55
56    fn test_assert_eq(first: &str, second: &str) {
57        let dir = DirGuard::new(
58            std::env::temp_dir().join(format!("assert-or-bless-{}", fastrand::u32(0..1_000_000))),
59        );
60        let snapshot_path = dir.0.join("test-snapshot.txt");
61        assert_eq_or_bless_if(first, &snapshot_path, true);
62        assert_eq_or_bless(second, &snapshot_path);
63    }
64
65    #[test]
66    fn assert_eq_succeeds() {
67        test_assert_eq(
68            "this is the\ncorrect snapshot contents",
69            "this is the\ncorrect snapshot contents",
70        );
71    }
72
73    #[test]
74    #[should_panic]
75    fn assert_eq_panics() {
76        test_assert_eq(
77            "this is the\ncorrect snapshot contents",
78            "this is the\nWRONG snapshot contents",
79        );
80    }
81}