Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/339.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java List.contains对象具有双精度公差_Java_Unit Testing_Collections_Floating Point_Floating Point Comparison - Fatal编程技术网

Java List.contains对象具有双精度公差

Java List.contains对象具有双精度公差,java,unit-testing,collections,floating-point,floating-point-comparison,Java,Unit Testing,Collections,Floating Point,Floating Point Comparison,假设我有一门课: public class Student { long studentId; String name; double gpa; // Assume constructor here... } 我有一个测试,比如: List<Student> students = getStudents(); Student expectedStudent = new Student(1234, "Peter Smith", 3.89) Assert(studen

假设我有一门课:

public class Student
{
  long studentId;
  String name;
  double gpa;

  // Assume constructor here...
}
我有一个测试,比如:

List<Student> students = getStudents();
Student expectedStudent = new Student(1234, "Peter Smith", 3.89)
Assert(students.contains(expectedStudent)
List students=getStudents();
学生期望学生=新生(1234,“彼得·史密斯”,3.89)
断言(students.contains)(expectedStudent)
现在,如果getStudents()方法将Peter的GPA计算为3.8899999994,那么该测试将失败,因为3.8899999994!=3.89

我知道我可以用对单个双精度/浮点值的容差来做一个断言,但是有没有一种简单的方法可以用“contains”来做这项工作,这样我就不必单独比较学生的每个字段(我将编写许多类似的测试,我将测试的实际类将包含更多的字段)

我还需要避免修改相关类(即Student)以添加自定义相等逻辑

另外,在我的实际类中,将有其他双值的嵌套列表,需要使用容差进行测试,如果我必须单独断言每个字段,这将使断言逻辑更加复杂

理想情况下,我想说“告诉我此列表是否包含此学生,对于任何浮点/双精度字段,请使用.0001的公差进行比较”


如果您想使用
包含
等于
,那么您需要注意
学生
等于
方法的四舍五入


但是,我建议使用适当的断言库,如AssertJ。

列表.contains()
的行为是根据元素的
equals()
方法定义的。因此,如果您的
Student.equals()
方法比较GPA是否精确相等,并且您无法更改它,则
List.contains()
对于您的目的来说不是一种可行的方法

可能
Student.equals()
不应该使用与容差的比较,因为很难看出如何使该类的
hashCode()
方法与这样的
equals()
方法一致

也许你能做的是编写一个替代方法,
equals
-like方法,比如“
matches()
”,它包含你的模糊比较逻辑。然后你可以测试一个学生的列表,该学生符合你的标准,比如

Assert(students.stream().anyMatch(s -> expectedStudent.matches(s)));

这里面有一个隐式的迭代,但是对于
List.contains()

我对GPA的概念不是特别熟悉,但我可以想象它从来不会超过小数点后2位。3.88999999994 GPA根本没有多大意义,或者至少没有意义

实际上,您正面临着人们在存储货币价值时经常面临的相同问题。3.89英镑是有意义的,但3.889999英镑不是。已经有大量信息可用于处理此问题。例如,请参阅

TL;DR:我会将数字存储为整数。因此3.88 GPA将存储为388。当您需要打印值时,只需除以
100.0
。整数与浮点值没有相同的精度问题,因此您的对象自然更易于比较。

1)不要仅出于单元测试目的重写equals/hashCode

Assert(students.contains(expectedStudent)
这些方法具有语义,并且它们的语义没有考虑类的所有字段,从而使测试断言成为可能

2)依靠测试库来执行断言

Assert(students.contains(expectedStudent)
或者(发表在约翰·博林格的回答中):

在单元测试方面是非常好的反模式。
当断言失败时,您需要做的第一件事是知道错误的原因以更正测试。
依靠布尔值来断言列表比较根本不允许这样做。
KISS(保持简单和愚蠢):使用测试工具/功能进行断言,不要重新发明轮子,因为它们将在测试失败时提供所需的反馈。

3)不要断言
double
相等(预期、实际)

Assert(students.contains(expectedStudent)
要断言双值,单元测试库在断言中提供第三个参数,以指定允许的增量,例如:

public static void assertEquals(double expected, double actual, double delta) 
在JUnit5中(JUnit4也有类似的功能)

或者选择更适合这种比较的
BigDecimal
double/float

但它不能完全解决您的需求,因为您需要断言实际对象的多个字段。使用循环来实现这一点显然不是一个好的解决方案。
Matcher库提供了一种有意义且优雅的方法来解决这个问题

4)使用Matcher库对实际列表对象的特定属性执行断言

使用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(...),
          );
//给定
...
//什么时候
List students=getStudents();
//然后
断言。断言(学生)
//双精度值的允许差值为0.1
.使用ComparatorForType(新的DoubleComparator(0.1),Double.class)
.Extraction(Student::getId、Student::getName、Student::getGpa)
.containsExactly(元组(1234,“彼得·史密斯”,3.89),
元组(…),
);
一些解释(所有这些都是AssertJ功能):

  • 使用ComparatorForType()
    允许为给定类型的元素或其字段设置特定的比较器

  • DoubleComparator
    是一个AssertJ比较器,提供在双重比较中考虑ε的便利

  • 提取
    定义要从列表中包含的实例中断言的值

  • containsExactly()
    断言提取的值与
    元组中定义的值完全相同(即不多、不少且顺序准确)

没有