下面通过实例演示如何在控制器中使用 MockMvc 进行单元测试。
package com.example.demo.controller; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HelloController { @RequestMapping("/hello") public String hello(String name) { return "hello " + name; } }
package com.example.demo.controller; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.web.WebAppConfiguration; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import org.springframework.test.web.servlet.result.MockMvcResultHandlers; import org.springframework.test.web.servlet.result.MockMvcResultMatchers; import org.springframework.test.web.servlet.setup.MockMvcBuilders; import org.springframework.web.context.WebApplicationContext; import static org.junit.Assert.*; @SpringBootTest @RunWith(SpringRunner.class) public class HelloControllerTest { //启用web上下文 @Autowired private WebApplicationContext webApplicationContext; private MockMvc mockMvc; @Before public void setUp() throws Exception{ //使用上下文构建mockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); } @Test public void hello() throws Exception { // 得到MvcResult自定义验证 // 执行请求 MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get("/hello") .contentType(MediaType.APPLICATION_JSON_UTF8) //传入参数 .param("name","longzhonghua") //接收的类型 .accept(MediaType.APPLICATION_JSON_UTF8)) //等同于Assert.assertEquals(200,status); //判断接收到的状态是否是200 .andExpect(MockMvcResultMatchers.status().isOk()) //等同于 Assert.assertEquals("hello longzhonghua",content); .andExpect(MockMvcResultMatchers.content().string("hello longzhonghua")) .andDo(MockMvcResultHandlers.print()) //返回MvcResult .andReturn(); //得到返回代码 int status = mvcResult.getResponse().getStatus(); //得到返回结果 String content = mvcResult.getResponse().getContentAsString(); //断言,判断返回代码是否正确 Assert.assertEquals(200,status); //断言,判断返回的值是否正确 Assert.assertEquals("hello longzhonghua",content); } }
运行测试,在控制器中会输出以下结果:
MockHttpServletRequest: HTTP Method = GET Request URI = /hello Parameters = {name=[longzhonghua]} Headers = [Content-Type:"application/json;charset=UTF-8", Accept:"application/json;charset=UTF-8"] Body = null Session Attrs = {} Handler: Type = com.example.demo.controller.HelloController Method = public java.lang.String com.example.demo.controller.HelloController.hello(java.lang.String) Async: Async started = false Async result = null Resolved Exception: Type = null ModelAndView: View name = null View = null Model = null FlashMap: Attributes = null MockHttpServletResponse: Status = 200 Error message = null Headers = [Content-Type:"application/json;charset=UTF-8", Content-Length:"18"] Content type = application/json;charset=UTF-8 Body = hello longzhonghua Forwarded URL = null Redirected URL = null Cookies = []
在上述结果中可以看到 访问方式、路径、参数、访问头、ModelAndView、FlashMap、MockHttpServletResponse。
本实例演示如何在 Service 中使用 Assert 进行单元测试。
package com.example.demo.entity; import lombok.Data; import lombok.Getter; import lombok.Setter; @Data public class User { private String name; private int age; }
这里用 @Service 来标注服务类,并实例化一个 User 对象。
package com.example.demo.service; import com.example.demo.entity.User; import org.springframework.stereotype.Service; @Service public class UserService { public User getUserInfo(){ User user = new User(); user.setName("pipi"); user.setAge(18); return user; } }
编写测试用于比较实例化的实体 User 和测试预期值是否一样。
package com.example.demo.service; import com.example.demo.entity.User; import org.junit.Assert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.context.web.WebAppConfiguration; import static org.hamcrest.CoreMatchers.*; //表明要在测试环境运行,底层使用的junit测试工具 @RunWith(SpringRunner.class) // SpringJUnit支持,由此引入Spring-Test框架支持! //启动整个spring的工程 @SpringBootTest public class UserServiceTest { @Autowired private UserService userService; @Test public void getUserInfo() { User user = userService.getUserInfo(); //比较实际的值和用户预期的值是否一样 Assert.assertEquals(18, user.getAge()); Assert.assertThat(user.getName(), is("pipixia")); } }
运行测试,结果显示出错,表示期望的值和实际的值不一样。
Repository 层主要用于对数据进行增加、删除、修改和查询操作、它相当于仓库管理员的进出货操作。
下面通过实例演示如何在 Repository 中进行单元测试,以及使用 @Transactional 注解进行回滚操作。
package com.example.demo.repository; import com.example.demo.entity.Card; import org.junit.After; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.annotation.Rollback; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.GetMapping; import java.util.List; import static org.junit.Assert.*; @RunWith(SpringRunner.class) @SpringBootTest @Transactional public class CardRepositoryTest { @Autowired private CardRepository cardRepository; @Test public void testQuery() { // 查询操作 List list = cardRepository.findAll(); for (Card card : list) { System.out.println(card); } } @Test public void testRollBack() { // 查询操作 Card card = new Card(); card.setNum(3); cardRepository.save(card); //throw new RuntimeException(); } }
testRollBack
方法:执行添加一条记录。如果开启了 @Transactional,则会在添加之后进行回滚,删除刚添加的数据,如果注释掉 @Transactional,则完成添加后不回滚。大家在测试时可以尝试去掉或添加 @Transactional 状态下的不同效果。这里的 @Transactional 放在类上,也可以加在方法上作用于方法。运行 testQuery
测试,控制台输出如下:
运行 testRollBack
测试,并添加 @Transactional,控制台输出如下:
上述结果表示先添加,然后操作被立即回滚了。
运行 testRollBack
测试,去掉 @Transactional,控制台输出如下:
注:设置了 id
为自增键。