本文对应源码:欢迎关注我的公众号nrsc,并在同名文章中获取本文对应源码。

@Spy和@Mock到底有什么区别?相信读完本文,你一定会真真切切地明白。

1 先明确一下具体问题

假设有下面这样一个技术方案:

该方案对应的伪代码如下,那该如何对下面的方法进行写单测呢?

@Autowired
private List<CompensateJudge> compensateJudgeList;

public Object calculateCompensateAmount(Object object) {
    //判断是否满足赔付条件,
    //如果有不满足的赔付条件,拿到不满足的具体详情
    List<CompensateJudgeDetail> failureDetails =
            compensateJudgeList.stream()
                    //判断是否要赔付
                    .map(j -> j.judge(object))
                    //过滤出不满足赔付的详情
                    .filter(r -> !r.isLegal())
                    .collect(Collectors.toList());
    
    if (!CollectionUtils.isEmpty(failureDetails)) {
        log.info("该广告主本周期内无需赔付,并记录无需赔付的具体原因");
        return "无需赔付";
    }

    log.info("该广告主本周期内需要赔付,并计算赔付金额;");
    return "需赔付,已计算赔付金额";
}

2 使用@Mock

这里直接给出使用@Mock写的单测代码:

public class AccountCompensateBizServiceTest {

    @Mock
    CompensateJudge compensateJudge;
    @Mock //使用@Mock创建的是一个完全模拟对象,它的任何方法都不会被真正调用,
    // 都需要模拟,本方案就需要对compensateJudgeList的stream()进行模拟
    List<CompensateJudge> compensateJudgeList;
    @Mock
    Logger log;
    @InjectMocks
    AccountCompensateBizService accountCompensateBizService;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testCalculateCompensateAmount001() throws Exception {
        CompensateJudgeDetail detail = new CompensateJudgeDetail();
        detail.setLegal(true);
        detail.setReason(null);
        //模拟compensateJudge的judge方法
        when(compensateJudge.judge(any())).thenReturn(detail);
        //模拟compensateJudgeList的stream()方法
        //【注意!!!】伪代码是用lambda表达式写的,如果用的for循环,这里可就不好mock了
        when(compensateJudgeList.stream()).thenReturn(Stream.of(compensateJudge));
        Object res = accountCompensateBizService.calculateCompensateAmount("object");
        Assert.assertEquals(res.toString(), "需赔付,已计算赔付金额");
    }

    @Test
    public void testCalculateCompensateAmount002() throws Exception {
        CompensateJudgeDetail detail = new CompensateJudgeDetail();
        detail.setLegal(false);
        detail.setReason("XXX");
        //模拟compensateJudge的judge方法
        when(compensateJudge.judge(any())).thenReturn(detail);
        //模拟compensateJudgeList的stream()方法,
        //【注意!!!】伪代码是用lambda表达式写的,如果用的for循环,这里可就不好mock了
        when(compensateJudgeList.stream()).thenReturn(Stream.of(compensateJudge));
        Object res = accountCompensateBizService.calculateCompensateAmount("object");
        Assert.assertEquals(res.toString(), "无需赔付");
    }
}

【注意!!!】伪代码是用lambda表达式写的,如果用的for循环,这里可就不好mock了。

3 使用@Spy

当@Mock不好用的情况下,我们还可以使用@Spy,这里也直接给出使用@Spy写的单测代码:

public class AccountCompensateBizServiceTest2 {

    @Mock
    CompensateJudge compensateJudge;
    @Spy
    //使用@Spy创建的对象,其方法会执行真实逻辑,
    // 因此也可以调用List对象的add方法为其add元素
    // 但@Mock却不行(任何方法都不会被真正调用,都需要模拟)
    List<CompensateJudge> compensateJudgeList = new ArrayList<>();
    @Mock
    Logger log;
    @InjectMocks
    AccountCompensateBizService accountCompensateBizService;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);

    }

    @Test
    public void testCalculateCompensateAmount001() throws Exception {
        CompensateJudgeDetail detail = new CompensateJudgeDetail();
        detail.setLegal(true);
        detail.setReason(null);
        when(compensateJudge.judge(any())).thenReturn(detail);
        //为compensateJudgeList添加一个mock的对象
        compensateJudgeList.add(compensateJudge);
        Object res = accountCompensateBizService.calculateCompensateAmount("object");
        Assert.assertEquals(res.toString(), "需赔付,已计算赔付金额");
    }

    @Test
    public void testCalculateCompensateAmount() throws Exception {
        CompensateJudgeDetail detail = new CompensateJudgeDetail();
        detail.setLegal(false);
        detail.setReason("XXX");
        when(compensateJudge.judge(any())).thenReturn(detail);
        //为compensateJudgeList添加一个mock的对象
        compensateJudgeList.add(compensateJudge);
        Object res = accountCompensateBizService.calculateCompensateAmount("object");
        Assert.assertEquals(res.toString(), "无需赔付");
    }
}

贴一下单测覆盖率

文章至此,想必你对@Mock和@Spy肯定有了更加深刻的认识。

4 留给读者

  • 当@Autowired注解了Map对象时如何写单测???

    读完上面的文章想必对你来说肯定是小case了,所以这个问题留给读者!😁😁😁

本文由mdnice多平台发布


nrsc
1 声望0 粉丝