Spring Boot测试
Spring 提供的了一个test框架,用于系统的集成测试。在Spring Boot中,你可以通过spring-boot-starter-test 来启动测试框架。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Junit测试
进行简单单元测试而不需要Spring Boot特性,可以直接使用Junit编写测试单元。
@BeforeClass和@AfterClass在类被实例化前(构造方法执行前)就被调用了,而且只执行一次,通常用来初始化和关闭资源。
@Before和@After和在每个@Test执行前后都会被执行一次。
@Test标记一个方法为测试方法单元,被@Ignore标记的测试方法不会被执行。
JUnit4为了保证每个测试方法都是单元测试,是独立的互不影响。所以每个测试方法执行前都会重新实例化测试类。
Spring Boot测试
当测试中需要使用Spring Boot功能时,可以使用@SpringBootTest注解,装配Spring上下文。
当作为普通Spring单元测试时,可以使用@ContextConfiguration,装配Spring 上下文。
基于Spring 的Junit测试单元需要使用@RunWith(SpringJUnit4ClassRunner.class)注解,该注解能够让Junit运行在Spring测试环境中,得到Spring上下文支持。在4.3版本中提供了等同于SpringJUnit4ClassRunner.class的简写类SpringRunner.class。
Spring Boot测试单元启动流程如下:
- 如果未指定classes 参数或者指定的classes参数不是启动main函数入口SpringBootTest(classes = SpringTestAutoConfig.class) ,则会自动从当前测试类包一层一层向上检索,直到找到@SpringBootApplication或@SpringBootConfiguration注释类为止。以此来启动Spring Boot应用,并装载Spring上下文。
- 如果未检索到Spring Boot启动主配置类,则会抛出异常:java.lang.IllegalStateException: Unable to find a @SpringBootConfiguration, you need to use @ContextConfiguration or @SpringBootTest(classes=…) with your test
- 如果制定的classes为普通Configer配置类,则会以此配置初始化Spring 上下文,而不会加载其他Bean到Spring 容器。可以在Junit测试单元中使用这些类。
@RunWith(SpringRunner.class)
@SpringBootTest(classes = {YourApplication.class})
public class BeanInjectTest {
// ...
}
Spring MVC 测试
如果要对Spring MVC 测试,可以使用@WebMvcTest 注解,可以在不需要完整启动HTTP服务器就可以快速测试MVC控制器。
使用@WebMvcTest注解时需要注意,只有部分bean会被加载到Spring 上下文中,分别是:
- @Controller
- @ControllerAdvice
- @JsonComponent
- Filter
- WebMvcConfigurer
- HandlerMethodArgumentResolver
其他常规的@Component @Bean @Service注解的Bean并不会加载到Spring测试环境。如果要使用这些类,需要使用mock打桩的方式。
import static org.mockito.BDDMockito.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
@RunWith(SpringRunner.class)
@WebMvcTest(HelloController.class)
public class HelloControllerTest {
@Autowired
private MockMvc mvc;
@MockBean
private HelloService helloService;
@Test
public void testSayHi() throws Exception {
// 模拟 HelloService.sayHi() 调用, 返回 "=== Hi ==="
when(helloService.sayHi()).thenReturn("=== Hi ===");
mvc.perform(get("/hello/sayHi"))
.andExpect(status().isOk())
.andDo(print());
}
}
Spring Boot Web测试
开启Spring Http服务对Web应用测试,可以使用注解@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)注解开启一个随机的可用端口。Spring Boot针对REST接口调用提供了TestRestTemplate模版。他可以解析链接服务器的接口地址。
@RunWith(SpringRunner.class)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationTest {
@Autowired
private TestRestTemplate restTemplate;
@Test
public void testSayHello() {
Map<String, Object> result = restTemplate.getForObject("/hello/sayHello", Map.class);
System.out.println(result.get("message"));
}
}
Spring Data JPA测试
对Spring Data JPA进行测试可以使用@DataJpaTest注解。@DataJpaTest使用的是内存数据库进行测试,你无需配置和启用真实的数据。在pom配置文件中添加依赖即可。
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
@DataJpaTest注解只会扫描@Entity 的Bean 和装配 Spring Data JPA 存储库,其他常规@Service @Component @Repository 注解的Bean不会加载到Spring 上下文中。
@RunWith(SpringRunner.class)
@DataJpaTest
public class UserRepositoryInMemoryTest {
@Autowired
private UserRepository userRepository;
@Test
public void testSave() {
User user = new User();
user.setName("fanlychie");
userRepository.save(user);
System.out.println("====================================");
System.out.println(userRepository.findAll());
System.out.println("====================================");
}
}
@Entity(name = "User")
public class User {
@Id
@GeneratedValue(generator = "uuidGenerator")
@GenericGenerator(name = "uuidGenerator", strategy = "uuid")
private String id;
private String name;
// getters and setters
}
public interface UserRepository extends JpaRepository<User, String> {
}
真实数据库测试
如果需要使用真实的数据库作为测试,需要添加注解@AutoConfigureTestDatabase(replace = Replace.NONE),通知Spring不要替换原有应用的默认数据库。
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
public class UserRepositoryMySQLTest {
@Autowired
private UserRepository userRepository;
@Test
public void testSave() {
User user = new User();
user.setName("fanlychie");
userRepository.save(user);
System.out.println("====================================");
System.out.println(userRepository.findAll());
System.out.println("====================================");
}
}
事务控制
默认情况下,在每个jpa测试执行完成时,都会执行事务回滚。从而防止测试数据污染数据库,如果不希望回滚可以在测试方法上添加注解@Rollback(false)。也可以在方法或者类上添加@Transactional作为全局的事务控制。
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = Replace.NONE)
public class UserRepositoryMySQLTest {
@Autowired
private UserRepository userRepository;
@Test
@Transactional(readOnly = true)
public void testSelect() {
System.out.println("====================================");
System.out.println(userRepository.findAll());
System.out.println("====================================");
}
}