繁体   English   中英

如何为 Comparator 代码覆盖率编写 Junit 测试用例

[英]How to write Junit test cases for Comparator code coverage

我有一个使用 Comparator 接口的排序机制,如下所示:

    public class SortByEmployeeIdByDesc implements Comparator<Employee> {

    public int compare(Employee a, Employee b)
    {
        return b.getEmpId().compareTo(a.getEmpId());
    }

   }

这是我的员工类:

    @Builder
    @AllArgsConstructor
    @NoArgsConstructor
    public class Employee {
      @Getter
      private String empId;

      private String empName;

      private Date createdTime;

      @Getter
      private Date lastUpdatedTime;
    }

我为上述比较器编写了如下测试用例:

    @ExtendWith(MockitoExtension.class)
    public void EmployeeSortTest{

    @Test
    public void sortEmpDescloyeeTest() {
    
    Employee emp1 = new Employee("abc", "test", "2020-10-10", "2010-10-10");
    Employee emp2 = new Employee("xyz", "test2", "2022-03-04", "2022-03-04");
    
    List<Employee> sortEmp = new ArrayList<>();
    sortEmp.add(emp1);
    sortEmp.add(emp2);
    
    Collections.sort(sortEmp, new SortByEmployeeIdByDesc());
    assertEquals(emp1, sortEmp.get(1));
}
}

上面的测试用例运行成功,但是当我在 Sonar 中签入时,它并没有覆盖代码。 是否有更好的 Junit 测试方法以使覆盖率增加到至少 90%?

我对语言版本和依赖项做了一些假设并运行您的代码:

  • Java 8+(语言 8 级)
  • JUnit 5(木星)

您的new Employee(...)构造函数不起作用,因为您的@AllArgsConstructor期望Date对象而不是String

假设您使用的是 Java8+,您应该使用LocalDate而不是Date

代码覆盖率产生以下结果。

Employee.java:50%未覆盖的行:

@Builder
@NoArgsConstructor
// omitted for brevity
@Getter
private LocalDate lastUpdatedTime;

SortByEmployeeIdByDesc:100%

要覆盖新行,您应该考虑按如下方式更改测试类(例如):

@ExtendWith(MockitoExtension.class)
class SortByEmployeeIdByDescTest {

    private static final LocalDate MIN_CREATED = LocalDate.of(2020, 10, 10);
    private static final LocalDate MIN_LASTUPDATED = LocalDate.of(2020, 10, 10);

    @Test
    void sortEmpDescloyeeTest() {
        Employee emp1 = new Employee("abc", "test", MIN_CREATED, MIN_LASTUPDATED);
        Employee emp2 = new Employee("xyz", "test2", LocalDate.of(2022, 3, 4), LocalDate.of(2022, 3, 4));

        List<Employee> sortEmp = new ArrayList<>();
        sortEmp.add(emp1);
        sortEmp.add(emp2);

        sortEmp.sort(new SortByEmployeeIdByDesc());
        assertEquals(emp1, sortEmp.get(1));
    }

    @Test
    void noArgsConstructorTest() {
        Employee employee = new Employee();
        assertNull(employee.getEmpId());
        assertNull(employee.getLastUpdatedTime());
    }

    @Test
    void builderTest() {
        Employee expectedEmployee = new Employee("testId", "test", MIN_CREATED, MIN_LASTUPDATED);
        Employee employee = Employee.builder()
            .empId("testId")
            .empName("test")
            .createdTime(MIN_CREATED)
            .lastUpdatedTime(MIN_LASTUPDATED)
            .build();
        assertEquals(expectedEmployee.getEmpId(), employee.getEmpId());
        assertEquals(expectedEmployee.getLastUpdatedTime(), employee.getLastUpdatedTime());
    }
}

因为您只在其中两个字段上声明@Getter ,所以您无法在builderTest中测试其他字段。

如果要比较所有字段,可以执行以下操作:

  1. 在 Employee 上添加@EqualsAndHashCode注释 -

     @Builder @AllArgsConstructor @NoArgsConstructor @EqualsAndHashCode // this annotation adds equals and hashCode methods public class Employee // omitted for brevity
  2. 更改builderTest代码如下 -

     @Test void builderTest() { Employee expectedEmployee = new Employee("testId", "test", MIN_CREATED, MIN_LASTUPDATED); Employee employee = Employee.builder() .empId("testId") .empName("test") .createdTime(MIN_CREATED) .lastUpdatedTime(MIN_LASTUPDATED) .build(); assertEquals(expectedEmployee, employee); // you now use equals // instead of testing // each field // individually }

两种方法(有或没有@EqualsAndHashCode注释)都产生 100% 的代码覆盖率。 请记住,除非您的 getter 和 setter 中有逻辑,否则覆盖它们通常被认为是微不足道的,因为您本质上是在测试 Java 赋值,人们可能认为这是可行的。

要测试比较器,第一个想法可能是向列表中添加一些项目,对列表进行排序,然后确保项目已正确排序。

但是,这种方法有一个可怕的缺点:对列表进行排序会执行尽可能少的比较,这与您尝试完成的操作完全相反。 另外,根据排序算法,您根本无法确定比较了哪些组合。

我曾经写过一个ComparatorTester ,它获取一个元素列表,然后将每个元素与每个元素进行比较。 它还确保比较器是自反的、反对称的和传递的。

当我在 Google 上搜索 ComparatorTester 时,我发现Firebase 的一个实现与我自己的实现非常相似,因为它使用了完全相同的想法。 这就是你应该遵循的方法。

在您的琐碎用例中,您甚至不需要编写自己的比较器类,这是 Java 8 之前的事情。如今,按单个属性排序变成了单线:

employees.sort(Comparator.comparing(Employee::getEmpId).reversed());

Comparator.compare(T o1, T o2) javadoc 状态(强调添加):

返回:整数、整数,因为第一个参数小于、等于或大于第二个参数。

因此,您的测试至少需要涵盖上述三个条件:

class SortByEmployeeIdByDescTest {

    @Test
    void compareEmployees() {
        final List<Employee> employees = Stream.of("2", "1", "3", "2")
                .map(id -> Employee.builder().empId(id).build())
                .collect(Collectors.toList());

        Collections.sort(employees, new SortByEmployeeIdByDesc());

        assertThat(employees).extracting(Employee::getEmpId)
                .containsExactly("3", "2", "2", "1");
    }
}

我在此示例中使用AssertJ ,强烈建议在您的项目中使用它。

此外,您的员工 ID 是一个字符串。 因此,您需要测试它何时为null 然后您将获得所需的覆盖范围。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM