#assertions #assert #nlp #pattern-matching

rassert

流畅、易于扩展的测试断言库

4个稳定版本

1.3.0 2021年10月2日
1.2.0 2021年10月2日
1.1.0 2021年10月2日
1.0.0 2021年9月30日

#645 in 测试

MIT许可协议

31KB
549

rassert

🦀 ❓ ❗

Rust的流畅、易于扩展的测试断言

rassert uses The MIT License. Current crates.io version. Continuous Integration status.

功能

  • 流畅的期望。在编写测试时,我们追求清晰和可读性。rassert通过提供流畅的期望接口,允许将断言尽可能接近自然语言。请参阅编写断言
  • 人性化的失败。在失败时,rassert会显示测试表达式、位置、实际值和预期值/条件,以帮助调试。
  • 模式匹配支持。使用expect_matches!宏来检查表达式是否匹配某些模式,例如expect_matches!(result, Ok(..))
  • 惰性评估和软评估。断言是惰性评估的,只有当通过conclude_panicconclude_result函数调用关闭期望链时才会进行评估。在软模式(通过soft()调用开启)下,期望链评估不会在第一个失败时停止。请参阅编写断言
  • 易于扩展。通过自定义期望轻松扩展rassert。请参阅自定义期望

快速开始

首先,将rassert添加到您的Cargo.toml文件中的dev-dependencies部分

[dev-dependencies]
rassert = "1"

然后在测试中简单地导入prelude模块

#[cfg(test)]
mod tests {
    use rassert::prelude::*;

    #[test]
    fn rassert_works() {
        expect!(&true)
            .not()
            .to_be_false()
            .and()
            .to_equal(true)
            .conclude_panic();
    }
}

编写断言

期望链

在rassert中,可以以期望链的形式编写断言。此类链允许对同一表达式编写多个期望。有两种所谓的入口点,可以开始链

  • expect!(expression)
    • 创建一个新的链,断言提供的expression
  • expect_matches!(expression,pattern)
    • 从提供的 expression 创建一个新的链,并自动向链中添加一个期望,断言 expressionpattern 匹配。

一旦启动了链,就可以在链上调用期望,如下所示

let v = vec![10];

expect!(&v)
    .to_be_non_empty()
    .and()
    .to_contain(10);

注意,and() 调用不是强制的,它只用于提高可读性。

结束链

由于 rassert 惰性评估期望,上述链将不执行任何操作。链只有在结束时才会断言指定的期望

// Will panic on a failed expectation.
expect!(&true)
    .to_be(false)
    .conclude_panic();

// Will return Result<(), String> containing the error
// message on failure.
let res = expect!(&true)
  .to_be(false)
  .conclude_result();

软模式

可以通过在结束链之前调用 soft() 将链置于软模式

let v = vec![10];

expect!(&v)
    .to_contain(15)
    .and()
    .to_contain(20)
    .soft()
    .conclude_panic();

软链在第一次失败时不会恐慌/返回,而是会运行每个断言,并呈现每个发生的失败的合并报告。

否定期望

可以使用 not() 函数否定一个随后的期望

expect!(&true)
    .not()
    .to_be_false()
    .conclude_panic();

如果想要否定更多的期望,那么需要再次应用 not()

可用期望

  • T
  • T,其中 T: Debug + PartialEq
    • to_equalto_be
    • to_not_equalto_not_be
  • 布尔值
    • to_be_true
    • to_be_false
  • Option<T>
    • to_be_some
    • to_be_none
  • Option<T>,其中 T: Debug + PartialEq
    • to_contain
  • 结果<T, E>
    • to_be_ok
    • to_be_err
  • Result<T, E>,其中 T: Debug + PartialEq
    • to_be_ok_with
  • Vec<T>
    • to_have_length
    • to_be_empty
    • to_be_non_empty
  • Vec<T>,其中 T: Debug + PartialEq
    • to_contain

自定义期望

自定义期望可以通过在 rassert 提供的 ExpectationChain 类型上编写 扩展特质 来编写。以下通过一个示例展示了如何编写自定义期望。

假设,我们想要编写一个针对自定义结构体 Pizza 的期望

#[derive(Debug)]
pub struct Pizza {
    pub flavor: String
}

在 rassert 中,期望实际上是遵循某种类似 命令模式 的结构体。给定,我们想要检查我们有什么口味的比萨,我们可以创建如下所示的内容

use rassert::Expectation;

struct ExpectPizzaFlavor {
    expected_flavor: String
}

impl Expectation<Pizza> for ExpectPizzaFlavor {
    fn test(&self, actual: &Pizza) -> bool {
        actual.flavor.eq(&self.expected_flavor)
    }

    fn message(&self, expression: &str, actual: &Pizza) -> String {
        format!("Expected {:?}\n  to have flavor {}\n  but had {}.", expression, self.expected_flavor, actual.flavor)
    }
}

实现 Expectation 特质需要两个函数

  • fn(&self,actual) -> bool
    • 实际的断言,在成功时返回 true,否则返回 false。参数 actual 对应于链中正在测试的值。
  • fn(&self, expression, actual) -> String
    • 当期望失败时显示的消息。expression 参数是 expect!/expect_matches! 宏的字符串化表达式参数。

一旦我们写好了期望结构,我们就可以最终扩展 ExpectationChain 类型

use rassert::ExpectationChain;

pub trait PizzaExpectationsExt<'a> {
    fn to_have_flavor(self, expected: &str) -> ExpectationChain<'a, Pizza>;
}

impl<'a> PizzaExpectationsExt for ExpectationChain<'a, Pizza> {
    fn to_have_flavor(self, expected: &str) -> ExpectationChain<'a, Pizza> {
        self.expecting(ExpectPizzaFlavor {
            expected_flavor: expected.to_owned()
        })
    }
}

上述代码片段最重要的部分如下

  • 扩展特质必须接受一个泛型生命周期参数,并在从期望函数返回的 ExpectationChain 类型中使用它。此参数对应于链中保持的不变引用的生命周期。然后,此引用指向实际测试的值。
  • 期望函数必须接受 self,因为期望链是 消耗构建器
  • 期望函数可以使用 expecting() 函数通过新的期望扩展链。此函数接受一个期望,当完成链时将执行。期望的字段可以用来参数化实际的断言。

然后,使用上述期望就像

let pizza = Pizza {
  flavor: "Hawaii".to_owned(),
};

expect!(&pizza)
  .to_have_flavor("Margherita")
  .conclude_panic();

src/expectations 目录中的内置期望也使用上述功能,因此它们是编写自定义期望的绝佳起点。

许可证

根据 MIT 许可证 授权

无运行时依赖