单元测试

本篇主要介绍了Spring Boot中创建单元测试,分别讲解了Service层单元测试、Controller层基于MockMvc的单元测试。

快速导航

添加maven依赖

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-test</artifactId>
  4. <scope>test</scope>
  5. </dependency>

创建测试类

Spring Boot单元测试是在/src/test/java目录下,可以选择手动创建测试类,另外还可以借助IDE快速创建,在需要创建测试的文件上点击头部菜单栏选择/Navigate/Test或者快捷键⇧⌘T(Mac电脑)来创建,如下所示:

单元测试 - 图1

单击Test之后会出现一个小弹窗提示 Create New Test...,如下所示:

单元测试 - 图2

点击 Create New Test...,弹出如下窗口所示

单元测试 - 图3

点击ok创建测试类 UserSerciveTest.java

  1. package com.angelo.service;
  2. import static org.junit.Assert.*;
  3. public class UserServiceTest {
  4. }

service单元测试

创建service/UserServiceTest.java 类,在类名上加上以下两个注解,就可以让一个普通类变成一个单元测试类。

  1. @RunWith(SpringRunner.class)@RunWith是一个运行器,SpringRunner.class表示使用Spring Test进行单元测试,其中SpringRunner继承类SpringJUnit4ClassRunner
  2. @SpringBootTest:将启动整个Spring Boot工程

service/UserServiceTest.java

  1. package com.angelo.service;
  2. import com.angelo.aspect.HttpAspect;
  3. import com.angelo.domain.User;
  4. import org.junit.Assert;
  5. import org.junit.Test;
  6. import org.junit.runner.RunWith;
  7. import org.slf4j.Logger;
  8. import org.slf4j.LoggerFactory;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.boot.test.context.SpringBootTest;
  11. import org.springframework.test.context.junit4.SpringRunner;
  12. @RunWith(SpringRunner.class)
  13. @SpringBootTest
  14. public class UserServiceTest {
  15. @Autowired
  16. private UserService userService;
  17. private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class);
  18. @Test
  19. public void findByIdTest() {
  20. User user = userService.findById(1);
  21. // 断言,是否和预期一致
  22. Assert.assertEquals("张三", user.getName());
  23. Assert.assertEquals(new Integer(-1), user.getAge());
  24. }
  25. }

在测试类上右击选择Run 'UserServiceTest',可以看到以下运行结果我们期望的user.getAge()为18,但是实际返回的是-1

单元测试 - 图4

controller单元测试

以上是针对业务层测试,如果想进行接口API测试怎么办呢,难道,开发完成每次调用postman一个个测吗?答案当然是no,不过,你也可以选择一个个测试没什么问题,如果你想通过代码实现模拟http请求就要用到我们的@AutoConfigureMockMvc注解,使用了MockMvc无需启动项目,就可实现接口测试。

以下用到的MockMvc方法介绍

  • mockMvc.perform:执行请求
  • MockMvcRequestBuilders.get:还支持post、put、delete等
  • contentType(MediaType.APPLICATION_JSON_UTF8):表示请求传输的Conent-Type=application/json;charset=utf-8
  • accept(MediaType.APPLICATION_JSON)):客户端希望接收的Conent-Type=application/json;
  • andExpect(MockMvcResultMatchers.status().isOk()):返回响应状态是否为期望的200,如果不是则抛出异常
  • andReturn():返回结果
  1. ```java
  2. package com.angelo.controller;
  3. import com.angelo.domain.User;
  4. import com.google.gson.Gson;
  5. import com.google.gson.GsonBuilder;
  6. import org.junit.Test;
  7. import org.junit.runner.RunWith;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
  10. import org.springframework.boot.test.context.SpringBootTest;
  11. import org.springframework.http.MediaType;
  12. import org.springframework.mock.web.MockHttpServletRequest;
  13. import org.springframework.mock.web.MockHttpServletResponse;
  14. import org.springframework.test.context.junit4.SpringRunner;
  15. import org.springframework.test.web.servlet.MockMvc;
  16. import org.springframework.test.web.servlet.MvcResult;
  17. import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
  18. import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
  19. import static org.junit.Assert.*;
  20. @RunWith(SpringRunner.class)
  21. @SpringBootTest
  22. @AutoConfigureMockMvc
  23. public class UserControllerTest {
  24. @Autowired
  25. private MockMvc mockMvc;
  26. /**
  27. * 查询用户列表
  28. * @throws Exception
  29. */
  30. @Test
  31. public void userListTest() throws Exception {
  32. String url = "/user/list/false";
  33. MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.get(url))
  34. .andExpect(MockMvcResultMatchers.status().isOk())
  35. .andReturn();
  36. MockHttpServletResponse response = mvcResult.getResponse();
  37. System.out.println(response.getStatus()); // 获取响应状态码
  38. System.out.println(response.getContentAsString()); // 获取响应内容
  39. }
  40. /**
  41. * 创建测试用户
  42. * @throws Exception
  43. */
  44. @Test
  45. public void userAddTest() throws Exception {
  46. User user = new User();
  47. user.setName("测试姓名");
  48. user.setAge(22);
  49. Gson gson = new GsonBuilder().setPrettyPrinting().create();
  50. MvcResult mvcResult = mockMvc.perform(MockMvcRequestBuilders.post("/user")
  51. .contentType(MediaType.APPLICATION_JSON_UTF8)
  52. .content(gson.toJson(user))
  53. .accept(MediaType.APPLICATION_JSON))
  54. .andReturn();
  55. MockHttpServletResponse response = mvcResult.getResponse();
  56. System.out.println(response.getStatus()); // 获取响应状态码
  57. System.out.println(response.getContentAsString()); // 获取响应内容
  58. }
  59. /**
  60. * 根据id删除用户测试
  61. * @throws Exception
  62. */
  63. public void deleteUserByIdTest() throws Exception {
  64. Integer id = 11;
  65. mockMvc.perform(MockMvcRequestBuilders.delete("/user/" + id))
  66. .andExpect(MockMvcResultMatchers.status().isOk());
  67. }
  68. }

运行结果如下,可以看到左边为本次测试涉及到的测试方法及时间,右边控制台分别打印了创建用户日志、及删除用户日志信息。其中,删除用户因为id=11不存在,因此抛出了一个异常。

单元测试 - 图5

问题汇总

org.hibernate.LazyInitializationException: could not initialize proxy [com.angelo.domain.User#1] - no Session

原因是懒加载的问题,因为hibernate的机制是当我们查询一个对象的时候,在默认情况下,返回的只是该对象的普通属性,当用户去使用对象属性的时候,才会向数据库再一次查询,可是,这时session已经关闭了,无法对数据库进行查询。

解决方案: SpringBoot配置文件设置spring.jpa.properties.hibernate.enable_lazy_load_no_trans=true

application.yml

  1. spring:
  2. jpa:
  3. hibernate:
  4. ddl-auto: update
  5. show-sql: true
  6. database: mysql
  7. properties:
  8. hibernate:
  9. enable_lazy_load_no_trans: true

Github查看本文完整示例 chapter5-1