Stub(桩)
代码中不包含逻辑,作为替身只返回固定数据:
public class LogStub implement Logger {
public String getLevel() {
return 'DEBUG';
}
}
Fake(伪装者)
Fake 是更加接近于生产行为的替身,如 h2database
Spy(间谍)
对于没有返回值的方法:
public class EmailSender {
public void send(User user) {
// 发邮件给用户...
}
}
public class UserService {
private EmailSender emailSender;
public UserService(EmailSender emailSender) {
this.emailSender = emailSender;
}
public void register(User user) {
// 注册逻辑...
emailSender.send(user);
}
}
在用户注册之后需要给用户发邮件,虽然发邮件不是我们关注的点,但我们仍然关心是否被成功调用,邮件的 send() 没有返回值,此时需要放出小间谍来帮助我们来完成测试:
class EmailSenderSpy extends EmailSender {
private Boolean called = false;
public boolean getCalled() {
return this.called;
}
@Override
public void sender(User user) {
this.called = true;
}
}
// 测试部分
@Test
public void should_called_send_email_to_user() {
EmailSenderSpy spy = new EmailSenderSpy();
UserService userService = new UserService(spy);
User user = new User(...);
assertEquals(false, spy.getCalled());
userService.register(user);
assertEquals(true, spy.getCalled());
}
Mock(模仿)
根据特定条件,返回特定的值,以验证代码的执行结果是否正确
Mock 与 Spy 的区别
当我们对 @Mock 的类,如
@Mock
private OrderDao dao;
进行模拟方法时,会像下面这样去做:
when(dao.getOrder()).thenReturn("returened by mock ");
但如果想对 @Spy 的类
@Spy
private PriceService ps;
进行模拟方法时,需要像下面一样去做:
doReturn("twotwo").when(ps).getPriceTwo();
原因:
使用 @Mock 生成的类,所有方法都是模拟的,而且返回值都是 NULL
使用 @Spy 生成的类,所有方法都是真实方法,返回值都是和真实方法一样的
所以,你用 when 去设置模拟返回值时,它里面的方法会先执行一次
使用 doReturn 去设置的话,就不会产生上面的问题,因为有 when 来进行控制要模拟的方法,所以不会执行原来的方法
覆盖率上的区别
使用 @mock 模拟私有方法测试代码如下:
@RunWith(PowerMockRunner.class)
@PrepareForTest({Calculator.class})
public class CalculatorTest {
@Test
public void testSumXX() throws Exception {
Calculator cal = PowerMockito.mock(Calculator.class);
PowerMockito.when(cal,"sumXX",anyInt(),anyInt()).thenReturn(2);
//指明callSumXX调用真实的方法
PowerMockito.when(cal.callSumXX(anyInt(),anyInt())).thenCallRealMethod();
assertEquals(2, cal.callSumXX(1, 2));
}
}
因为 @mock 出来的对象可能已经发生了变化,调用的方法都不是真实的,@mock 出来的 Calculator 对应已经不是原来的 Calculator,在进行覆盖率统计时统计出来的 Calculator 类覆盖率为 0.00%
使用 @spy 模拟私有方法测试代码如下:
@RunWith(PowerMockRunner.class)
@PrepareForTest({Calculator.class})
public class CalculatorTest {
@Test
public void testSumXX() throws Exception {
Calculator cal = PowerMockito.spy(new Calculator());
PowerMockito.doReturn(2).when(cal,"sumXX",anyInt(),anyInt());
assertEquals(2, cal.callSumXX(1, 2));
}
}
因为 @spy 使用的真实的 Calculator 对象实例,调用的都是真实的方法,所以通过这种方式进行测试,在进行覆盖率统计时统计出来的 Calculator 类覆盖率为 50%
综上,通过 spy 的方式既能隔离环境依赖又能统计出覆盖率