赞
踩
如果饿了就吃,困了就睡,渴了就喝,人生就太无趣了
源码地址:https://github.com/keer123456789/springbootstudy/tree/master/mybatisdemo
单元测试是编写单元测试类,针对类级别的测试。比如使用Junit框架,针对一个类,写一个测试类,测试目标类的大部分主要方法。
需要注意单元测试的级别是方法。项目当中,类之间的依赖调用是很常见的事,如果你要测试一个类,而这个目标类又调用了另一个类,那么在测试时就没有遵守“在一个类范围内进行测试”,自然算不得单元测试。
如图1:A、B、C、D类存在依赖关系,如果对A类进行单元测试,就需要采取Mock方式对依赖B类C类进行模拟。
此次使用的项目使用springboot+mybatis对数据库进行增删改查操作的功能,分别对项目的Controller
,Service
, Dao
三层进行单元测试。
由于controller
类相较于其他bean
,功能比较特殊,负责接收HTTP请求,返回HTTP消息,但是单元测试不能手动发送HTTP请求,所以使用@WebMvcTest
注解对测试类进行注解。然后使用MockMvc
模拟请求。
项目中的PeopleController
控制器,其中不仅接收了外部HTTP请求,而且对PeopleService
存在依赖,不仅要对请求进行mock,还要对PeopleService
进行模拟
@RestController public class PeopleController { protected Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired PeopleService peopleService; @GetMapping("/getAllPeopleInfo") public WebResult getAllPeopleInfo() { logger.info("接收到请求:/getAllPeopleInfo"); return peopleService.getAllPeopleInfo(); } @PostMapping("/addPeopleInfo") public WebResult addPeopleInfo(@RequestBody People people) { logger.info("接收到请求:/getAllPeopleInfo"); return peopleService.addPeopleInfo(people); } //other method…… }
PeopleService
进行Mock,使用Mockito
进行模拟。 @MockBean
PeopleService peopleService;
@Before
public void setup() {
WebResult webResult = new WebResult();
webResult.setStatus(WebResult.SUCCESS);
Mockito.when(peopleService.getAllPeopleInfo()).thenReturn(webResult);
Mockito.when(peopleService.addPeopleInfo(Mockito.any())).thenReturn(webResult);
Mockito.when(peopleService.getPeopleInfoByID(Mockito.anyInt())).thenReturn(webResult);
Mockito.when(peopleService.updatePeopleNameByID(Mockito.anyString(), Mockito.anyInt())).thenReturn(webResult);
Mockito.when(peopleService.deletePeopleInfoByID(Mockito.anyInt())).thenReturn(webResult);
}
因为PeopleService
中的方法都是返回WebResult
实例。对于返回的WebResult
实例,模拟返回status
属性为0(SUCCESS)
public class WebResult<T> { public static final int SUCCESS = 0; public static final int ERROR = 1; private int status; private T data; private String message; public WebResult() { } public int getStatus() { return status; } public void setStatus(int status) { this.status = status; } public T getData() { return data; } public void setData(T data) { this.data = data; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
.andExpect(MockMvcResultMatchers.status().isOk())
是判断请求状态是否正确。第二个.andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("0")));
是对PeopleService
中的返回值进行测试判断。@RunWith(SpringRunner.class) @DisplayName("人员API接口测试") @WebMvcTest(PeopleController.class) public class PeopleControllerTest { @Autowired private MockMvc mockMvc; @MockBean PeopleService peopleService; @Before public void setup() { WebResult webResult = new WebResult(); webResult.setStatus(WebResult.SUCCESS); Mockito.when(peopleService.getAllPeopleInfo()).thenReturn(webResult); Mockito.when(peopleService.addPeopleInfo(Mockito.any())).thenReturn(webResult); Mockito.when(peopleService.getPeopleInfoByID(Mockito.anyInt())).thenReturn(webResult); Mockito.when(peopleService.updatePeopleNameByID(Mockito.anyString(), Mockito.anyInt())).thenReturn(webResult); Mockito.when(peopleService.deletePeopleInfoByID(Mockito.anyInt())).thenReturn(webResult); } @Test @DisplayName(value = "测试获取全部信息接口") public void testGetAllPeopleInfo() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/getAllPeopleInfo")) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()) .andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("0"))); } @Test @DisplayName(value = "增加人员接口") public void testAddPeopleInfo() throws Exception { Gson gson = new Gson(); People people = new People("java", 1, 12, "spring"); String json = gson.toJson(people); mockMvc.perform(MockMvcRequestBuilders .post("/addPeopleInfo") .contentType(MediaType.APPLICATION_JSON) .content(json)) .andExpect(MockMvcResultMatchers.status().isOk()) .andDo(MockMvcResultHandlers.print()) .andExpect(MockMvcResultMatchers.content().string(Matchers.containsString("0"))); } //other method…… }
针对这种问题:采用是通过使用内部类来自定义配置。内部类只有一个@SpringBootApplication注解,指定了扫描的根路径,以缩小bean的扫描范围。
@RunWith(SpringRunner.class)
@DisplayName("人员API接口测试")
@WebMvcTest(PeopleController.class)
public class PeopleControllerTest {
@SpringBootApplication(scanBasePackages = {"com.keer.mybatisdemo.controller"})
static class InnerConfig {
}
@Autowired
private MockMvc mockMvc;
@MockBean
PeopleService peopleService;
}
service层的依赖只有Dao层的mapper,所以只需要对底层的PeopleMappermapper
进行mock模拟。
@Service public class PeopleServiceImpl implements PeopleService { protected Logger logger = LoggerFactory.getLogger(this.getClass()); PeopleMapper peopleMapper; @Override public WebResult getAllPeopleInfo() { WebResult webResult = new WebResult(); List<People> list = peopleMapper.getAllPeopleInfo(); webResult.setStatus(WebResult.SUCCESS); webResult.setMessage("select all people info success "); webResult.setData(list); logger.info("select all people info success ,data:" + list.toString()); return webResult; } @Override public WebResult addPeopleInfo(People people) { WebResult webResult = new WebResult(); if (peopleMapper.addPeopleInfo(people) == 1) { webResult.setData(1); webResult.setMessage("add people info success"); webResult.setStatus(WebResult.SUCCESS); logger.info("add people info success"); } else { webResult.setStatus(WebResult.ERROR); webResult.setMessage("add people info fail"); webResult.setData(0); logger.error("add people info fail"); } return webResult; } //other method…… }
因为没有启动spring容器,@Autowird
自动注入功能消失,此时采取@InjectMocks
进行bean的注入
@InjectMocks
创建一个PeopleServiceImpl
实例,将mock 的bean注入该实例中。@Mock
模拟一个bean。@RunWith(SpringRunner.class) public class PeopleServiceImplTest { @InjectMocks private PeopleServiceImpl peopleService; @Mock private PeopleMapper peopleMapper; @Before public void setup() { People bob = new People("bob", 1, 15, "北京"); People alex = new People("alex", 2, 20, "天津"); People john = new People("john", 3, 25, "湖北"); List<People> allPeople = Arrays.asList(john, bob, alex); Mockito.when(peopleMapper.getPeopleInfoByID(alex.getId())).thenReturn(alex); Mockito.when(peopleMapper.getPeopleInfoByID(-1)).thenReturn(null); Mockito.when(peopleMapper.getAllPeopleInfo()).thenReturn(allPeople); Mockito.when(peopleMapper.updatePeopleNameByID("alexChange", alex.getId())).thenReturn(1); Mockito.when(peopleMapper.deletePeopleInfoByID(john.getId())).thenReturn(1); } @Test @DisplayName(value = "输入正确id查看返回结果是否正确") public void whenValidId_thenPeopleShouldBeFound() { int alexID = 2; WebResult webResult = peopleService.getPeopleInfoByID(alexID); People people = (People) webResult.getData(); Assertions.assertThat(people.getId()).isEqualTo(alexID); Mockito.verify(peopleMapper, VerificationModeFactory.times(1)).getPeopleInfoByID(Mockito.anyInt()); } @Test @DisplayName(value = "插入人员信息") public void addPeopleInfo_thenReturnSuccess() { People bob = new People("bob", 1, 15, "北京"); Mockito.when(peopleMapper.addPeopleInfo(bob)).thenReturn(1); WebResult webResult = peopleService.addPeopleInfo(bob); Assertions.assertThat(webResult.getStatus()).isEqualTo(WebResult.SUCCESS); Mockito.verify(peopleMapper, VerificationModeFactory.times(1)).addPeopleInfo(Mockito.any()); } //other mothed…… }
因为这一层大多数都是数据库操作,需要配置数据连接,使用mybatis还需要配置,所以在写测试时加上注解引入配置
@SpringBootTest
注解负责扫描配置来构建测试用的Spring上下文环境@EnableAutoConfiguration
自动加载配置到容器中@Transactional
数据库操作的回滚功能,不会在数据库中产生脏数据。@RunWith(SpringRunner.class) @DisplayName("人员接口测试") @EnableAutoConfiguration @SpringBootTest @Transactional public class PeopleMapperTest { @Autowired PeopleMapper peopleMapper; @Test @DisplayName("增加人员信息") public void testAddPeopleInfo() { People people = new People("keer", 1, 25, "湖北武汉加油!!"); Assert.assertEquals(1, peopleMapper.addPeopleInfo(people)); } @Test @DisplayName("根据主键id查询人员信息") public void testGetPeopleInfoByID() { People people = new People("keer", 1, 25, "湖北武汉加油!!"); Assert.assertEquals(1, peopleMapper.addPeopleInfo(people)); Assert.assertEquals("keer", peopleMapper.getPeopleInfoByID(1).getName()); } //other method…… }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。