测试替身(Test Double)

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 的方式既能隔离环境依赖又能统计出覆盖率

你可能感兴趣的:(测试替身(Test Double))