单元测试的目的
单元测试的目的,是为了提高代码质量,保证代码的可维护性。因此我们在编写单元测试的时候,更多的是对代码逻辑的验证,查看
是否能够对一些边界条件,特殊的逻辑完整的覆盖到。
但是很多时候,我们的业务代码会进行一些IO操作(DB,CACHE,REMOTE-RPC),这样导致单元测试的成本比较高。面对这样的场景的时候,
我们可以通过Mock来简化我们的单元测试。
由于单元测试最主要的是要覆盖业务逻辑,所以我们可以通过mock我们想要的一些边界值,来覆盖代码的各个业务逻辑分支,查看代码
的处理是否和我们的预期一致。
为什么通过PowerMock进行单元测试的编写
PowerMock是在基于Mockito的基础上,对mock功能扩充对静态方法,私有方法的mock处理。这样在在书写单元测试的时候,大大的降低了编写的成本。
如何通过PowerMock进行单元测试的编写
-
例如我们需要进行单元测试的类:
@Service public class A { @Autowired private B b; private String privateMethod(String test) { System.out.println("private method"); return "ok" } public String pubMethod(String test) { String var = b.getVar(test); return this.privateMethod(var); } }
编写单元测试的时候,我们需要对A类进行测试,执行其中的public方法,同时需要mock掉对应的私有方法。
在编写单元测试之前,我们需要知道injectMocks和Mock的区别 @InjectMocks 用来注解需要测试的类。 @Mock 用来注解需要测试的类中依赖的其他类。
@RunWith(PowerMockRunner.class) public class ATest{ @InjectMocks private A a; @Mock private B b; @Before public void setup() { a = PowerMockito.spy(new A())); MockitoAnnotations.initMocks(this); } @Test public void pubMethodTest() { PowerMockito.doReturn("test").when(a, "privateMethod", "test"); PowerMockito.when(b.getVar("test")).thenReturn("test"); String res = a.pubMethod() assertEquals(res, "test") // check private method 调用的次数是否和预期一致 PowerMockito.verifyPrivate(a, Mockito.times(1)).invoke("privateMethod", "test"); } }
在上面的单元测试中,我们使用spy来对要测试的数据进行Mock,表示的是找个测试类中,有些方法我们需要mock,有些我们需要测试。如果不在setup中对A进行spy,在测试中我们就无法进行对私有方法的mock。
上述在setup进行的操作也可以通过注解进行,但是需要注意注解的顺序,这样mockito就会先进行mock,然后进行spy
@RunWith(PowerMockRunner.class) public class ATest { @InjectMocks @spy private A a; @Mock private B b; ...... }
当我们遇到如下需要测试的场景的时候,需要在测试类上加PrepareForTest注解。
- 当需要mock final方法的时候,必须加注解@PrepareForTest。注解@PrepareForTest里写的类是final方法所在的类。
- 当需要mock静态方法的时候,必须加注解@PrepareForTest。注解@PrepareForTest里写的类是静态方法所在的类。
- 当需要mock私有方法的时候, 需要加注解@PrepareForTest,注解里写的类是私有方法所在的类。测试类自己的私有方法可以不需要在这个注解中加。
- 当需要mock系统类的静态方法的时候,必须加注解@PrepareForTest。注解里写的类是需要调用系统方法所在的类。
具体的使用姿势如下:
@RunWith(PowerMockRunner.class) @PrepareForTest({B.class, C.class}) public class ATest { }