在导师公司打工,做了一个版本,技术上虽然没学到什么新东西,但是至少明白公司开发的各个流程,尤其是在提测阶段十分痛苦。一个bug反复出现,不停的修改,不停的调试,十分折腾。总结了一下异常处理没有放到controller层,log的使用还不够,另外明白了单元测试的重要性。从前到后找bug实在是太过痛苦,应当用单元测试把bug扼杀在摇篮里。特此总结单元测试的方法和用到的技术,争取下一个版本做到0bug提测。
包结构规范
单元测试包结构和源码结构保持一致
命名规范
文件名
被测试文件名+Test
方法名
test+方法名+情况说明
不同单元测试类规范
以下所有的单元测试都要使用assert进行断言,要注意分支覆盖率尽可能提高,基层基础测试类
基础测试类,用于启动Springboot环境
@RunWith(SpringRunner.class)
@SpringBootTest
public class MallProductApplicationTests {
@Before
public void setup() {
你的装载逻辑
}
}
setup方法
每一个测试类都要有一个setup方法并用@Before注解,该方法用于装载测试时需要的属性
Controller层
使用MockMvc模拟http请求(见后面教程)
public class TestAttrController extend MallProductApplicationTests{
@Test
public void AttrAddTest() {
使用MockMVC来构造请求
}
}
Service层
Dao层
注意加上注解@RollBack防止数据库污染,整个类加上@Transactional注解
Mock的使用
mock主要是在某个接口还尚未实现或者难以调用时来产生一个临时的实现类
MockMVC
MockMVC是Controller层测试时用于构造http请求模拟网络环境时用到的。由spring-test提供。在json请求场景中使用方法如下
Get请求示例
@WebAppConfiguration
public class MockTest extends GulimallProductApplicationTests {
private MockMvc mockMvc;
@Autowired
private WebApplicationContext wac;
@Before
public void setup() {
mockMvc = MockMvcBuilders.webAppContextSetup(wac).build();
}
@Test
public void testMockMvc() throws Exception {
MvcResult result = mockMvc.perform(
MockMvcRequestBuilders.get("/product/category/list"))
.andExpect(MockMvcResultMatchers.jsonPath("$.code")//断言json返回值
.value(200))
.andReturn();
String contentAsString = result.getResponse().getContentAsString();
System.out.println(contentAsString);
}
}
说明:setup用于向mockMvc传入当前web环境,可以使用mockmvc的.andExpect来进行断言,一般在json返回值中使用MockMvcResultMatchers.jsonPath静态方法断言json中的某个值是否满足,也可以通过下面的
result.getResponse().getContentAsString();方法直接得到json字符串做后续处理
post请求示例
@Test
public void testMockMvcPost() throws Exception {
AttrGroupDto attrGroupDto = new AttrGroupDto();
attrGroupDto.setAttrGroupName("a");
attrGroupDto.setCatelogId(1L);
attrGroupDto.setSort(1);
attrGroupDto.setAttrGroupName("bb");
attrGroupDto.setIcon("123123");
MvcResult result = mockMvc.perform(
MockMvcRequestBuilders.post("/product/attrgroup/add")
.content(JSON.toJSONString(attrGroupDto)).contentType(MediaType.APPLICATION_JSON))
.andReturn();
System.out.println(result.getResponse().getContentAsString());
}
使用mockito构造接口
Mockito已经包含在了springboot-test-starter里不需要额外引入
public class MockTest {
AttrDao attrDao;
@Before
public void setup() {
//初始化所有由mokito注解标注的对象
MockitoAnnotations.initMocks(this);
//mock AttrDao接口
attrDao = Mockito.mock(AttrDao.class);
AttrEntity attrEntity = new AttrEntity();
attrEntity.setAttrId(10L);
//定义行为
Mockito.when(attrDao.selectAttrById(1L)).thenReturn(attrEntity);
}
@Test
public void testMock() {
AttrEntity attrEntity = attrDao.selectAttrById(1L);
System.out.println(attrEntity);
}
}
说明:首先需要装载接口类,然后用Mockito.when().thenReturn方法定义接口中方法的行为即可实现mock调用
也可以使用注解注入
public class MockTest {
@Mock
AttrDao attrDao;
@Before
public void setup() {
//初始化所有由mokito注解标注的对象
MockitoAnnotations.initMocks(this);
//注解注入就不需要这句了
//attrDao = Mockito.mock(AttrDao.class);
AttrEntity attrEntity = new AttrEntity();
attrEntity.setAttrId(10L);
//定义行为
Mockito.when(attrDao.selectAttrById(1L)).thenReturn(attrEntity);
}
@Test
public void testMock() {
AttrEntity attrEntity = attrDao.selectAttrById(1L);
System.out.println(attrEntity);
}
}
Assert的使用
java自带的关键字assert十分僵硬,只能传入一个boolean值,而且断言失败后直接就推出了rollback也不能回滚,因此最好用junit中的Assert类
assertEqual
判断两者是否一致
@Test
public void assertEqualTest() {
// 成功
Assert.assertEquals(1L, 1L);
// 失败
Assert.assertEquals(1L, 2L);
int[] a1 = new int[]{1,2,3};
int[] a2 = new int[]{1,2,3};
int[] a3 = new int[]{2,3,4};
// 成功
Assert.assertArrayEquals(a1, a2);
//失败
Assert.assertArrayEquals(a1, a3);
}
assertTrue, assertFalse
直接判断boolean的值
@Test
public void testAssertTrueFalse() {
assertTrue(1 == 1);
assertFalse(1 == 2);
}
assertNull,assertNotNull
就不多说了
assertSame, assertNotSame
判断两者对象引用是否相同
assertThat
这个是重点,可以传入一个hamcrest提供的Matcher接口来进行判断
Assert.assertThat(obj, Matchers.xxx);