为了岩石Spring Boot 使用单元测试,我们将在本文中使用的应用程序是一个API,它提供了对员工信息的一些基本操作。这是一个典型的分层架构——API调用是从控制器处理到持久层的服务。
1,添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<version>1.5.3.RELEASE</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>test</scope>
<version>1.4.194</version>
</dependency>
H2 DB是一个内存数据,为了方便测试,省去对应的配置。
2,集成测试使用@DataJpaTest
员工基本信息:
@Entity
@Table(name = "person")
public class Employee {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
@Size(min = 3, max = 20)
private String name;
// standard getters and setters, constructors
}
数据访问层使用Spring Data JPA,持久化层的代码:
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
public Employee findByName(String name);
}
测试类:
@RunWith(SpringRunner.class)
@DataJpaTest
public class EmployeeRepositoryTest {
@Autowired
private TestEntityManager entityManager;
@Autowired
private EmployeeRepository employeeRepository;
// write test cases here
}
@RunWith(SpringRunner.class)
是用于spring boot test 和JUnit的连接桥梁,在使用单元测试时此注解时必须加的。
@DataJpaTest
为测试持久化层提供一系列标准的设置:
- 配置内存数据库
- 配置 Hibernate,Spring Data 和数据源
- 支持@EntityScan
打开SQL日志
The TestEntityManager提供了和标准JPA类似的操作数据的api方法,例如:
@Test public void whenFindByName_thenReturnEmployee() { // given Employee alex = new Employee("alex"); entityManager.persist(alex); entityManager.flush(); // when Employee found = employeeRepository.findByName(alex.getName()); // then assertThat(found.getName()) .isEqualTo(alex.getName()); }
3,使用@MockBean
我们的service层代码是依赖持久化层的,service层代码:
@Service
public class EmployeeServiceImpl implements EmployeeService {
@Autowired
private EmployeeRepository employeeRepository;
@Override
public Employee getEmployeeByName(String name) {
return employeeRepository.findByName(name);
}
}
在某些情况下,我们需要模拟程序的业务逻辑,而不需要通过持久层去操作库,此时我们就可以使用Spring Boot Test提供的Mock来做:
@RunWith(SpringRunner.class)
public class EmployeeServiceImplTest {
@TestConfiguration
static class EmployeeServiceImplTestContextConfiguration {
@Bean
public EmployeeService employeeService() {
return new EmployeeServiceImpl();
}
}
@Autowired
private EmployeeService employeeService;
@MockBean
private EmployeeRepository employeeRepository;
// write test cases here
}
为了实例化一个bean方便在测试类中使用@Autowired注入,可以使用@TestConfiguration
来实现。
接下来,通过mock的方式来mock持久化层:
@Before
public void setUp() {
Employee alex = new Employee("alex");
Mockito.when(employeeRepository.findByName(alex.getName()))
.thenReturn(alex);
}
@Test
public void whenValidName_thenEmployeeShouldBeFound() {
String name = "alex";
Employee found = employeeService.getEmployeeByName(name);
assertThat(found.getName())
.isEqualTo(name);
}
4,使用@WebMvcTest
单元测试
Controller:
@RestController
@RequestMapping("/api")
public class EmployeeRestController {
@Autowired
private EmployeeService employeeService;
@GetMapping("/employees")
public List<Employee> getAllEmployees() {
return employeeService.getAllEmployees();
}
}
测试类:
@RunWith(SpringRunner.class)
@WebMvcTest(EmployeeRestController.class)
public class EmployeeRestControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private EmployeeService service;
// write test cases here
}
在大多数情况下,@ WebMVC测试将仅限于引导单个控制器。它与 @MockBean一起用于提供所需依赖关系的模拟实现。@WebMvcTest还自动配置MockMvc,它提供了在不启动完整的HTTP服务器的情况下轻松测试MVC控制器的强大方法。
@Test
public void givenEmployees_whenGetEmployees_thenReturnJsonArray()
throws Exception {
Employee alex = new Employee("alex");
List<Employee> allEmployees = Arrays.asList(alex);
given(service.getAllEmployees()).willReturn(allEmployees);
mvc.perform(get("/api/employees")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(1)))
.andExpect(jsonPath("$[0].name", is(alex.getName())));
}
5,使用@SpringBootTest
集成测试
集成测试,故名思议,它是侧重集成应用程序的不同层,在实际开发中,我们应该将集成测试与单元测试分开,不应该与单元测试放到一起运行,我们可以通过使用不同的配置文件来做测试。
@RunWith(SpringRunner.class)
@SpringBootTest(SpringBootTest.WebEnvironment.MOCK, classes = Application.class)
@AutoConfigureMockMvc
@TestPropertySource(
locations = "classpath:application-integrationtest.properties")
public class EmployeeRestControllerIntegrationTest {
@Autowired
private MockMvc mvc;
@Autowired
private EmployeeRepository repository;
// write test cases here
}
@SpringBootTest的webEnvironment属性用来配置运行时环境;我们在这里使用Web..MOCK——这样容器将在模拟servlet环境中操作。
@ TestFrasyStand注释用来配置特定于我们测试的属性文件的位置。请注意,加载了@ TestPropertySource 源的属性文件将重写现有的Apvest.Frasic文件。
application-integrationtest.properties:
spring.datasource.url = jdbc:h2:mem:test
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect
如果你测试mysql,只需要修改次配置文件即可,如下测试类:
@Test
public void givenEmployees_whenGetEmployees_thenStatus200()
throws Exception {
createTestEmployee("bob");
mvc.perform(get("/api/employees")
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().isOk())
.andExpect(content()
.contentTypeCompatibleWith(MediaType.APPLICATION_JSON))
.andExpect(jsonPath("$[0].name", is("bob")));
}