Contents

Hamcrest 数字匹配器简介

1. 概述

Hamcrest 提供静态匹配器来帮助使单元测试断言更简单、更清晰。您可以在此处 开始探索一些可用的匹配器。

在本文中,我们将深入探讨与数字相关的匹配器。

2. 设置

要获得 Hamcrest,我们只需将以下 Maven 依赖项添加到我们的pom.xml中:

<dependency>
    <groupId>org.hamcrest</groupId>
    <artifactId>java-hamcrest</artifactId>
    <version>2.0.0.0</version>
</dependency>

最新的 Hamcrest 版本可以在Maven Central 上找到。

3. 邻近匹配器

我们要查看的第一组匹配器是检查某个元素是否接近某个值 +/- 一个错误的匹配器。

更正式地说:

value - error <= element <= value + error

如果上面的比较为真,则断言将通过。

让我们看看它的实际效果!

3.1. isClosedouble

假设我们有一个数字存储在一个名为actual 的双精度变量中。而且,我们要测试actual值是否接近 1 +/- 0.5。 那是:

1 - 0.5 <= actual <= 1 + 0.5
    0.5 <= actual <= 1.5

现在让我们使用isClose匹配器创建一个单元测试:

@Test
public void givenADouble_whenCloseTo_thenCorrect() {
    double actual = 1.3;
    double operand = 1;
    double error = 0.5;
 
    assertThat(actual, closeTo(operand, error));
}

由于 1.3 介于 0.5 和 1.5 之间,因此测试将通过。同样,我们可以测试负面情况:

@Test
public void givenADouble_whenNotCloseTo_thenCorrect() {
    double actual = 1.6;
    double operand = 1;
    double error = 0.5;
 
    assertThat(actual, not(closeTo(operand, error)));
}

现在,让我们看一下具有不同类型变量的类似情况。

3.2. isCloseBigDecimal

isClose是重载的,可以与双精度值一样使用,但使用BigDecimal对象**:

@Test
public void givenABigDecimal_whenCloseTo_thenCorrect() {
    BigDecimal actual = new BigDecimal("1.0003");
    BigDecimal operand = new BigDecimal("1");
    BigDecimal error = new BigDecimal("0.0005");
    
    assertThat(actual, is(closeTo(operand, error)));
}
@Test
public void givenABigDecimal_whenNotCloseTo_thenCorrect() {
    BigDecimal actual = new BigDecimal("1.0006");
    BigDecimal operand = new BigDecimal("1");
    BigDecimal error = new BigDecimal("0.0005");
    
    assertThat(actual, is(not(closeTo(operand, error))));
}

请注意,is匹配器只装饰其他匹配器,不添加额外的逻辑。它只是使整个断言更具可读性。

这就是邻近匹配器的内容。接下来,我们将看看订单匹配器。

4. 订单匹配器

正如他们的名字所说,这些匹配器有助于对订单进行断言。

其中有五个:

  • comparesEqualTo
  • greaterThan
  • greaterThanOrEqualTo
  • lessThan
  • lessThanOrEqualTo

它们几乎是不言自明的,但让我们看一些例子。

4.1. *Integer *数值的顺序匹配器

最常见的情况是将这些匹配器与 numbers一起使用。

所以,让我们继续创建一些测试:

@Test
public void given5_whenComparesEqualTo5_thenCorrect() {
    Integer five = 5;
    
    assertThat(five, comparesEqualTo(five));
}
@Test
public void given5_whenNotComparesEqualTo7_thenCorrect() {
    Integer seven = 7;
    Integer five = 5;
    assertThat(five, not(comparesEqualTo(seven)));
}
@Test
public void given7_whenGreaterThan5_thenCorrect() {
    Integer seven = 7;
    Integer five = 5;
 
    assertThat(seven, is(greaterThan(five)));
}
@Test
public void given7_whenGreaterThanOrEqualTo5_thenCorrect() {
    Integer seven = 7;
    Integer five = 5;
 
    assertThat(seven, is(greaterThanOrEqualTo(five)));
}
@Test
public void given5_whenGreaterThanOrEqualTo5_thenCorrect() {
    Integer five = 5;
 
    assertThat(five, is(greaterThanOrEqualTo(five)));
}
@Test
public void given3_whenLessThan5_thenCorrect() {
   Integer three = 3;
   Integer five = 5;
 
   assertThat(three, is(lessThan(five)));
}
@Test
public void given3_whenLessThanOrEqualTo5_thenCorrect() {
   Integer three = 3;
   Integer five = 5;
 
   assertThat(three, is(lessThanOrEqualTo(five)));
}
@Test
public void given5_whenLessThanOrEqualTo5_thenCorrect() {
   Integer five = 5;
 
   assertThat(five, is(lessThanOrEqualTo(five)));
}

有道理,对吧?请注意理解谓词断言的内容是多么简单。

4.2. String值的顺序匹配器

尽管比较数字完全有意义,但很多时候比较其他类型的元素很有用。这就是为什么顺序匹配器可以应用于任何实现Comparable接口的类。

让我们看一些String的例子:

@Test
public void givenBenjamin_whenGreaterThanAmanda_thenCorrect() {
    String amanda = "Amanda";
    String benjamin = "Benjamin";
 
    assertThat(benjamin, is(greaterThan(amanda)));
}
@Test
public void givenAmanda_whenLessThanBenajmin_thenCorrect() {
    String amanda = "Amanda";
    String benjamin = "Benjamin";
 
    assertThat(amanda, is(lessThan(benjamin)));
}

StringComparable接口的compareTo方法中实现字母顺序。

所以,“Amanda”这个词出现在“Benjamin”这个词之前是有道理的。

4.3. LocalDate值的订单匹配器

Strings一样,我们可以比较日期。让我们看一下我们在上面创建的相同示例,但使用了LocalDate对象:

@Test
public void givenToday_whenGreaterThanYesterday_thenCorrect() {
    LocalDate today = LocalDate.now();
    LocalDate yesterday = today.minusDays(1);
 
    assertThat(today, is(greaterThan(yesterday)));
}
@Test
public void givenToday_whenLessThanTomorrow_thenCorrect() {
    LocalDate today = LocalDate.now();
    LocalDate tomorrow = today.plusDays(1);
    
    assertThat(today, is(lessThan(tomorrow)));
}

很高兴看到声明*assertThat(today, is(lessThan(tomorrow)))*接近于普通英语。

4.4. 自定义类的顺序匹配器

那么,为什么不创建我们自己的类并实现Comparable 呢?这样,我们可以利用顺序匹配器与自定义顺序规则一起使用

让我们从创建一个Person bean 开始:

public class Person {
    String name;
    int age;
    // standard constructor, getters and setters
}

现在,让我们实现Comparable

public class Person implements Comparable<Person> {

    // ...
    @Override
    public int compareTo(Person o) {
        if (this.age == o.getAge()) return 0;
        if (this.age > o.getAge()) return 1;
        else return -1;
    }
}

我们的compareTo实现按年龄比较两个人。现在让我们创建几个新测试:

@Test
public void givenAmanda_whenOlderThanBenjamin_thenCorrect() {
    Person amanda = new Person("Amanda", 20);
    Person benjamin = new Person("Benjamin", 18);
 
    assertThat(amanda, is(greaterThan(benjamin)));
}
@Test
public void 
givenBenjamin_whenYoungerThanAmanda_thenCorrect() {
    Person amanda = new Person("Amanda", 20);
    Person benjamin = new Person("Benjamin", 18);
 
    assertThat(benjamin, is(lessThan(amanda)));
}

匹配器现在将基于我们的compareTo逻辑工作。

5. NaN 匹配器

Hamcrest 提供了一个额外的数字匹配器来定义一个数字是否实际上是一个数字

@Test
public void givenNaN_whenIsNotANumber_thenCorrect() {
    double zero = 0d;
    
    assertThat(zero / zero, is(notANumber()));
}