Joe*_*jr2 2 java floating-point collections unit-testing floating-point-comparison
假设我有这门课:
public class Student
{
long studentId;
String name;
double gpa;
// Assume constructor here...
}
Run Code Online (Sandbox Code Playgroud)
我有一个类似的测试:
List<Student> students = getStudents();
Student expectedStudent = new Student(1234, "Peter Smith", 3.89)
Assert(students.contains(expectedStudent)
Run Code Online (Sandbox Code Playgroud)
现在,如果 getStudents() 方法将 Peter 的 GPA 计算为类似于 3.8899999999994,那么该测试将失败,因为 3.8899999999994 != 3.89。
我知道我可以对单个双精度/浮点值做一个容忍度的断言,但是有没有一种简单的方法可以使这个与“包含”一起工作,这样我就不必单独比较 Student 的每个字段(我将编写许多类似的测试,而我将要测试的实际类将包含更多字段)。
我还需要避免修改有问题的类(即学生)以添加自定义相等逻辑。
此外,在我的实际课程中,将有其他 double 值的嵌套列表需要进行容差测试,如果我必须单独断言每个字段,这将使断言逻辑更加复杂。
理想情况下,我想说“请告诉我此列表是否包含该学生,对于任何浮点/双精度字段,请以 .0001 的容差进行比较”
感谢任何保持这些断言简单的建议。
1) 不要仅出于单元测试目的而覆盖 equals/hashCode
这些方法具有语义,并且它们的语义并未考虑类的所有字段以使测试断言成为可能。
2)依靠测试库来执行你的断言
Assert(students.contains(expectedStudent)
Run Code Online (Sandbox Code Playgroud)
或者那个(发布在 John Bollinger 的回答中):
Assert(students.stream().anyMatch(s -> expectedStudent.matches(s)));
Run Code Online (Sandbox Code Playgroud)
在单元测试方面是很好的反模式。
当断言失败时,您首先需要知道错误的原因以纠正测试。
依靠布尔值来断言列表比较根本不允许这样做。
KISS(保持简单和愚蠢):使用测试工具/功能来断言并且不要重新发明轮子,因为它们会在您的测试失败时提供所需的反馈。
3)不要断言double
用equals(expected, actual)
。
为了断言双精度值,单元测试库在断言中提供了第三个参数来指定允许的增量,例如:
public static void assertEquals(double expected, double actual, double delta)
Run Code Online (Sandbox Code Playgroud)
在 JUnit 5 中(JUnit 4 有类似的东西)。
或有利于BigDecimal
以double/float
更适合于这种比较。
但它不会完全解决您的要求,因为您需要断言实际对象的多个字段。使用循环来做到这一点显然不是一个好的解决方案。
Matcher 库提供了一种有意义且优雅的方式来解决这个问题。
4) 使用 Matcher 库对实际 List 的对象的特定属性进行断言
使用 AssertJ :
//GIVEN
...
//WHEN
List<Student> students = getStudents();
//THEN
Assertions.assertThat(students)
// 0.1 allowed delta for the double value
.usingComparatorForType(new DoubleComparator(0.1), Double.class)
.extracting(Student::getId, Student::getName, Student::getGpa)
.containsExactly(tuple(1234, "Peter Smith", 3.89),
tuple(...),
);
Run Code Online (Sandbox Code Playgroud)
一些解释(所有这些都是 AssertJ 特性):
usingComparatorForType()
允许为给定类型的元素或其字段设置特定的比较器。
DoubleComparator
是一个 AssertJ 比较器,提供了在双重比较中考虑 epsilon 的便利。
extracting
定义要从 List 中包含的实例断言的值。
containsExactly()
断言提取的值恰好是(不多也不少,并且按照确切的顺序)在Tuple
s 中定义的这些值。
归档时间: |
|
查看次数: |
893 次 |
最近记录: |