如何编写JUNIT测试案例以进行比较代码覆盖

发布于 2025-02-06 15:03:29 字数 1180 浏览 4 评论 0原文

我有一个使用比较器界面的分类机制,如下所示:

    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%?

I have a Sorting mechanism using Comparator interface like below:

    public class SortByEmployeeIdByDesc implements Comparator<Employee> {

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

   }

Here is my Employee class:

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

      private String empName;

      private Date createdTime;

      @Getter
      private Date lastUpdatedTime;
    }

I have written test cases like below for the above comparator:

    @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));
}
}

The above test case is running successfully, but when I am checking in Sonar, it's not covering the code. Is there a better Junit test approach so that the coverage increases to at least 90%?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

旧街凉风 2025-02-13 15:03:29

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

  • Java 8+(语言级别8)
  • Junit 5(jupiter)

您的new Employee(...)构造仪不起作用,因为您的@allargsconstructor期望date对象而不是String

假设您使用的是Java8+,则应使用localdate而不是date

代码覆盖范围得出以下结果。

员工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. 添加@equalsandhashcode雇员上的注释 -

      @builder
     @AllargSconstructor
     @noargsconstructor
     @equalsandhashcode //此注释添加了平等和哈希码方法
     公共班员工
     //简短省略
     
  2. 更改builderTest代码,如下所示 -

      @test
     void builderTest(){
         员工Expectee Employee =新员工(“ testID”,“ test”,min_created,min_lastupded);
         员工雇员=雇员.builder()
             .empid(“ testID”)
             .empname(“测试”)
             。
             。
             。建造();
         assertequals(预期员工,雇员); //您现在使用平等
                                                   //而不是测试
                                                   //每个字段 
                                                   //单独
     }
     

这两种方法(使用或不使用@equalsandhashcode注释)产生100%代码覆盖范围。请记住,除非您在守望者和二传手中具有逻辑,否则覆盖它们通常被认为是微不足道的,因为您本质上是在测试Java分配,这可能会假定工作。

I've made some assumptions about language version and dependencies and run your code:

  • Java 8+ (language level 8)
  • JUnit 5 (Jupiter)

Your new Employee(...) constructors weren't working because your @AllArgsConstructor is expecting a Date object not a String.

Assuming you're using Java8+, you should use LocalDate instead of Date.

Code coverage yields the following results.

Employee.java: 50%
Uncovered lines:

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

SortByEmployeeIdByDesc: 100%

To cover the new lines, you should consider changing your test class as follows (for example):

@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());
    }
}

Because you've only declared @Getter on two of the fields, you can't test the others in the builderTest.

If you want to compare all the fields you could do the following:

  1. Add the @EqualsAndHashCode annotation on Employee -

     @Builder
     @AllArgsConstructor
     @NoArgsConstructor
     @EqualsAndHashCode // this annotation adds equals and hashCode methods
     public class Employee
     // omitted for brevity
    
  2. Change the builderTest code as follows -

     @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
     }
    

Both approaches (with or without the @EqualsAndHashCode annotation) yield 100% code coverage. Bear in mind that unless you have logic in your getters and setters, covering them is usually considered trivial because you are essentially testing Java assignment, which one may assume to work.

绿萝 2025-02-13 15:03:29

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

但是,这种方法的缺点很差:排序列表会尽可能少地比较,这与您要完成的工作完全相反。另外,根据排序算法,您无法确定完全比较了哪些组合。

我曾经写过comparatterter,该列出了元素列表,然后将每个元素与每个元素进行比较。它还确保比较器是反射性,反对称性和及时性的。

当我谷歌搜索comparatortester时,我发现 firebase的实现,它与我自己的实现非常相似,因为它使用了完全相同的想法。这就是您应该遵循的方法。

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

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

To test a comparator, the first idea may be to add a few items to a list, sort the list and then ensure that the items have been sorted correctly.

This approach has a terrible downside though: sorting a list performs as few comparisons as possible, which is exactly the opposite of what you are trying to accomplish. Plus, depending on the sorting algorithm, you cannot be sure which combinations have been compared at all.

I once wrote a ComparatorTester that took a list of elements and then compared each element to each element. It also made sure that the comparator is reflexive, antisymmetrical and transitive.

When I googled for ComparatorTester, I found an implementation by Firebase that felt remarkably similar to my own implementation, as it uses the exact same idea. That's the approach you should follow.

In your trivial use case, you don't even need to write your own comparator class, that's a thing from before Java 8. Nowadays, sorting by a single property becomes a one-liner:

employees.sort(Comparator.comparing(Employee::getEmpId).reversed());
别想她 2025-02-13 15:03:29

comparator.compare(t o1,t o2) javadoc state (添加了强调):

返回:A 整数,正面整数
因为第一个论点小于,等于或大于
第二。

因此,在最小上,您的测试需要涵盖上述三个条件:

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。然后,您将获得所需的覆盖范围。

The Comparator.compare(T o1, T o2) javadoc states (emphasis added):

Returns: a negative integer, zero, or a positive integer
as the first argument is less than, equal to, or greater than the
second.

Therefore, at the minimum your test needs to cover the above three conditions:

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");
    }
}

I'm using AssertJ in this example and highly recommend using it in your projects.

In addition, your employee id is a String. So you need a test for when it's null. Then you will have the desired coverage.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文