单元测试规范
1. maven配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
其中这个类集成了
-
JUnit 4: 目前最强大的java应用单元测试框架
-
Spring Test & Spring Boot Test: Spring Boot 集成测试支持
-
AssertJ: 一个java断言库,提供测试断言支持
-
Hamcrest: 对象匹配断言和约束组件
-
Mockito: 可以按类型创建mock对象,可以根据方法参数指定特定的响应,也支持对于mock调用过程的断言
-
JSONassert: JSON断言库
-
JsonPath: JSON XPath 操作类库
2. 测试模板
2.1 sping自带的单元测试模板
@RunWith(SpringRunner.class)
@SpringBootTest
public class ApplicationTests {
@Test
public void contextLoads() {
}
//在下面我们可以写自己的单元测试
}
正常来说,SpringBoot项目建立的时候会生成一个测试类/src/test/ApplicationTests.java
不建议使用spring自带的单元测试模板
因为我们如果去执行这个测试类,会启动一个空的spring项目。也就是说,我们启动了整个应用,耗费了资源,只是为了测试一个方法。一个好的单元测试应该是毫秒级的。
2.2 使用Mockito框架
常用注解
- @RunWith(MockitoJUnitRunner.class) 使用在类上,初始化mock
- @Mock 创建一个mock
- @InjectMocks 创建一个实例,其余用@Mock注解创建的mock将被注入到用该实例中
常用测试场景
- do/when:包括doThrow(…).when(…)/doReturn(…).when(…)/doAnswer(…).when(…)
- given/will:包括given(…).willReturn(…)/given(…).willAnswer(…)
- when/then: 包括when(…).thenReturn(…)/when(…).thenAnswer(…)
其中:
when(…) thenReturn(…)会调用真实的方法,如果你不想调用真实的方法而是想要mock的话,就不要使用这个方法。
doReturn(…) when(…) 不会调用真实方法
Answer 修改对未预设的调用返回默认期望
@Test
public void mockTest(){
//mock对象使用Answer来对未预设的调用返回默认期望值
List list = mock(List.class,new Answer() {
@Override
public Object answer(InvocationOnMock invocation) throws Throwable {
return 999;
}
});
//下面的get(1)没有预设,通常情况下会返回NULL,但是使用了Answer改变了默认期望值
assertThat(list.get(1),is(999));
//下面的size()没有预设,通常情况下会返回0,但是使用了Answer改变了默认期望值
assertThat(list.size(),is(999));
}
模拟void方法
Mockito.doNothing().when(对象).方法名()
Mockito.doAnswer((invocationOnMock) -> null).when(对象).方法名();
demo
@RunWith(MockitoJUnitRunner.class)
public class HelloControllerTest {
//需要mock的对象
@Mock
HelloService helloService;
@InjectMocks
HelloController helloController ;
@Test
public void hello() throws Exception {
Mockito.when(helloService.hello()).thenReturn("hello");
String result=helloController.hello();
assertEquals("hello", result);
}
}
Mockito 更多的使用可查看→官方文档
##### 不足之处
-
不能mock静态方法
-
不能mock private方法
-
不能mock final class
2.3 PowerMockito
mavan配置
<!-- https://mvnrepository.com/artifact/org.powermock/powermock-module-junit4 -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-module-junit4</artifactId>
<version>2.0.5</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.powermock/powermock-api-mockito2 -->
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-api-mockito2</artifactId>
<version>2.0.4</version>
<scope>test</scope>
</dependency>
##### 类注解
@RunWith(PowerMockRunner.class) // 告诉JUnit使用PowerMockRunner进行测试
@PrepareForTest({DateUtils.class}) // 所有需要测试的类列在此处,适用于模拟final类或有final, private, static, native方法的类
mock静态方法
//mock DateUtils工具类
PowerMockito.mockStatic(DateUtils.class);
PowerMockito.when(DateUtils.getToday()).thenReturn("20200329");
mock私有方法
HelloService spy = PowerMockito.spy(helloService;
PowerMockito.when(spy, "testPrivate" ).thenReturn("test");//私有方法mockito不行了,需要用无所不能的PowerMock监视spy
String result=spy.hello("test");//该方法调用testPrivate方法
assertEquals("hello", result);