单元测试

一切准备就绪——让我们向域模块添加一些单元测试,以确保我们编写的所有代码都能按预期运行。

//! src/domain.rs
// [...]

#[cfg(test)]
mod tests {
    use claim::{assert_err, assert_ok};

    use crate::domain::SubscriberName;

    #[test]
    fn a_256_grapheme_log_name_is_valid() {
        let name = "a".repeat(256);
        assert_ok!(SubscriberName::parse(name));
    }

    #[test]
    fn a_name_longer_than_256_graphemes_is_rejected() {
        let name = "a".repeat(257);
        assert_err!(SubscriberName::parse(name));
    }

    #[test]
    fn whitespace_only_names_are_rejected() {
        let name = " ".to_string();
        assert_err!(SubscriberName::parse(name));
    }

    #[test]
    fn empty_string_is_rejected() {
        let name = "".to_string();
        assert_err!(SubscriberName::parse(name));
    }

    #[test]
    fn names_containing_an_invalid_character_are_rejected() {
        let name = "".to_string();
        assert_err!(SubscriberName::parse(name));
    }

    #[test]
    fn a_valid_name_is_parsed_successfully() {
        let name = "Zhangzhiyu".to_string();
        assert_ok!(SubscriberName::parse(name));
    }
}

不幸的是, 它不能编译 - cargo 突出显示了我们对 assert_ok/assert_err 的所有用法

error[E0277]: `SubscriberName` doesn't implement `std::fmt::Debug`
  --> src/domain.rs:64:9
   |
64 |         assert_err!(SubscriberName::parse(name));
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |         |
   |         `SubscriberName` cannot be formatted using `{:?}` because it doesn'
t implement `std::fmt::Debug`
   |         required by this formatting parameter
   |

声明需要我们的类型实现 Debug trait,以便提供这些友好的错误信息。让我们在 SubscriberName 上添加一个 #[derive(Debug)] 属性:

//! src/domain.rs
// [...]

#[derive(Debug)]
pub struct SubscriberName(String);

编译器现在应该没问题了。测试怎么样?

cargo test
running 6 tests
test domain::tests::empty_string_is_rejected ... FAILED
test domain::tests::a_valid_name_is_parsed_successfully ... ok
test domain::tests::names_containing_an_invalid_character_are_rejected ... FAILE
D
test domain::tests::whitespace_only_names_are_rejected ... FAILED
test domain::tests::a_256_grapheme_log_name_is_valid ... ok
test domain::tests::a_name_longer_than_256_graphemes_is_rejected ... FAILED

failures:

---- domain::tests::empty_string_is_rejected stdout ----

thread 'domain::tests::empty_string_is_rejected' panicked at src/domain.rs:19:13
:
 is not a valid subscriber name
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

---- domain::tests::names_containing_an_invalid_character_are_rejected stdout --
--

thread 'domain::tests::names_containing_an_invalid_character_are_rejected' panic
ked at src/domain.rs:19:13:
 is not a valid subscriber name

---- domain::tests::whitespace_only_names_are_rejected stdout ----

thread 'domain::tests::whitespace_only_names_are_rejected' panicked at src/domai
n.rs:19:13:
  is not a valid subscriber name

---- domain::tests::a_name_longer_than_256_graphemes_is_rejected stdout ----

thread 'domain::tests::a_name_longer_than_256_graphemes_is_rejected' panicked at
 src/domain.rs:19:13:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaa is not a valid subscriber name


failures:
    domain::tests::a_name_longer_than_256_graphemes_is_rejected
    domain::tests::empty_string_is_rejected
    domain::tests::names_containing_an_invalid_character_are_rejected
    domain::tests::whitespace_only_names_are_rejected

test result: FAILED. 2 passed; 4 failed; 0 ignored; 0 measured; 0 filtered out; 
finished in 0.00s

error: test failed, to rerun pass `--lib`

所有要求失败的路径测试都失败了,因为我们仍然在验证约束不满足的时候panic——让我们改变它:

//! src/domain.rs
// [...]
impl SubscriberName {
    pub fn parse(s: String) -> Result<SubscriberName, String> {
        // [...]

        if is_empty_or_whitespace || is_too_long || contains_forbidden_characters {
            // Replacing `panic!` with `Err(...)`
            Err(format!("{s} is not a valid subscriber name"))
        } else {
            Ok(Self(s))
        }
    }
}

现在,我们所有的领域单元测试都通过了——让我们最终解决本章开头编写的失败的集成测试。