You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写Comparator的JUnit用例提升Sonar代码覆盖率至90%

问题背景

基于Comparator<Employee>实现员工ID倒序排序逻辑,相关代码如下:

  • 排序器实现类SortByEmployeeIdByDesc
public class SortByEmployeeIdByDesc implements Comparator<Employee> {
    public int compare(Employee a, Employee b) {
        return b.getEmpId().compareTo(a.getEmpId());
    }
}
  • 关联Employee实体类,使用Lombok注解简化样板代码
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
  @Getter
  private String empId;
  private String empName;
  private Date createdTime;
  @Getter
  private Date lastUpdatedTime;
}
  • 原有基于JUnit5+Mockito编写的测试用例可正常执行通过,但Sonar代码质量检测时未统计到Comparator实现类的覆盖率,需要优化测试方案将覆盖率提升至90%以上
@ExtendWith(MockitoExtension.class)
public class 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));
    }
}
问题根因
  • 原有测试仅覆盖了compare方法中a.empId < b.empId(返回正数)的单一场景,没有覆盖返回负数、返回0的分支,本身覆盖率不足。
  • 依赖Collections.sort间接调用比较器的方式,会受JDK不同版本排序内部逻辑优化的影响,可能导致Sonar字节码插桩无法精准关联到自定义比较器的执行路径,出现覆盖率统计遗漏。
  • 原有测试代码存在隐性编译错误:Employee的createdTime、lastUpdatedTime为Date类型,直接传入字符串不符合类型要求。
  • 无Mock依赖的场景下强行加载Mockito扩展,属于不必要的测试开销。
优化后的测试方案

直接对比较器的compare方法做全分支断言,不依赖排序工具类间接调用,确保所有执行路径都被精准统计,代码如下:

import org.junit.jupiter.api.Test;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;

class EmployeeSortTest {
    private final SortByEmployeeIdByDesc idDescComparator = new SortByEmployeeIdByDesc();
    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

    @Test
    void compare_whenFirstEmpIdSmaller_shouldReturnPositive() throws Exception {
        // 覆盖场景:a.empId < b.empId,倒序场景下a应排在b后,返回正数
        Employee empSmall = Employee.builder()
                .empId("abc")
                .empName("test")
                .createdTime(dateFormat.parse("2020-10-10"))
                .lastUpdatedTime(dateFormat.parse("2010-10-10"))
                .build();
        Employee empLarge = Employee.builder()
                .empId("xyz")
                .empName("test2")
                .createdTime(dateFormat.parse("2022-03-04"))
                .lastUpdatedTime(dateFormat.parse("2022-03-04"))
                .build();

        assertTrue(idDescComparator.compare(empSmall, empLarge) > 0);
        // 同步验证排序结果符合预期
        List<Employee> empList = new ArrayList<>(List.of(empSmall, empLarge));
        empList.sort(idDescComparator);
        assertEquals(empLarge, empList.get(0));
        assertEquals(empSmall, empList.get(1));
    }

    @Test
    void compare_whenFirstEmpIdLarger_shouldReturnNegative() throws Exception {
        // 覆盖场景:a.empId > b.empId,倒序场景下a应排在b前,返回负数
        Employee empLarge = Employee.builder()
                .empId("xyz")
                .empName("test2")
                .createdTime(dateFormat.parse("2022-03-04"))
                .lastUpdatedTime(dateFormat.parse("2022-03-04"))
                .build();
        Employee empSmall = Employee.builder()
                .empId("abc")
                .empName("test")
                .createdTime(dateFormat.parse("2020-10-10"))
                .lastUpdatedTime(dateFormat.parse("2010-10-10"))
                .build();

        assertTrue(idDescComparator.compare(empLarge, empSmall) < 0);
        List<Employee> empList = new ArrayList<>(List.of(empLarge, empSmall));
        empList.sort(idDescComparator);
        assertEquals(empLarge, empList.get(0));
        assertEquals(empSmall, empList.get(1));
    }

    @Test
    void compare_whenEmpIdEqual_shouldReturnZero() throws Exception {
        // 覆盖场景:a.empId = b.empId,两元素顺序相等,返回0
        Employee emp1 = Employee.builder()
                .empId("abc")
                .empName("test")
                .createdTime(dateFormat.parse("2020-10-10"))
                .lastUpdatedTime(dateFormat.parse("2010-10-10"))
                .build();
        Employee emp2 = Employee.builder()
                .empId("abc")
                .empName("test_copy")
                .createdTime(dateFormat.parse("2022-03-04"))
                .lastUpdatedTime(dateFormat.parse("2022-03-04"))
                .build();

        assertEquals(0, idDescComparator.compare(emp1, emp2));
    }
}
优化效果
  • 直接调用比较器目标方法做断言,完全规避JDK排序内部逻辑对覆盖率统计的干扰,Sonar可以精准识别到所有执行代码。
  • 覆盖compare方法的三个全部分支,代码覆盖率可达100%,满足质量检测要求。
  • 修正原有测试的类型错误,使用Lombok Builder构造实体不需要记忆全参构造的参数顺序,代码可读性更高。
  • 移除不必要的Mockito扩展加载,测试启动速度更快。

内容的提问来源于stack exchange,提问作者Tech_sharma

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 21:09:30