如何写测试 八
对于一般的开发人员来说,Spring是一个解决了很多问题,非常优秀的框架,其解决了Java服务器端开发过程中,需要进行的打包并部署到容器的操作。
而在Spring这个框架之中,一方面提供了开发上的简易,另一方面也提供了测试上的方便,无论是单元测试和集成测试都可以完美支持。
更加具体的,则可以体现在Spring提供的核心功能,也就是依赖注入,在实际开发之中,通常会使用依赖注入来完成相关组件的组装。对于Spring的测试,也是基于了依赖注入,只要是依赖注入的组件,Spring都可以很好的提供测试。
这里我们给出两个反例
比如使用不基于字段的注入
@Service
public class TodoItemService { @Autowired private TodoItemRepository repository; } |
这里我们通过字段的方式进行了注入,这种方式会让这个Service在项目启动的时候自动初始化并将仓库类进行注入。
其支持和Spring的集成测试,但是对于单元测试却不是很友好。这里我们如果是改为将Autowired注解放在构造函数上,让Spring去初始化
@Service
public class TodoItemService { private final TodoItemRepository repository; @Autowired public TodoItemService(final TodoItemRepository repository) { this.repository = repository; } … } |
这里我们就可以在单元测试的时候像一个普通对象进行测试了。
其次是不要依赖ApplicationContext
在某些工程之中,有些代码会尝试利用ApplicationContext中的静态方法getBean来获取到Spring容器池中的Bean对象。
但是这种代码的使用,是一种错误的做法,其打破了DI容器的原本设计,另一方面还让核心代码对第三方代码进行了依赖。
所以总结一下,对于Spring的使用应该在代码层面不依赖于Spring,而不是深深的依赖Spring。
而Spring对于测试,还提供了一些专属的注解
比如@MockBean,其就类似
@BeforeEach
public void setUp() { this.repository = mock(TodoItemRepository.class); … } |
但是其在集成测试的时候,会提供依赖注入的能力。
对于Spring提供的集成测试,我们则可以从两个角度来看,就是之前说过的数据库和Web角度。
对于数据库的角度,我们之前曾经给过一个例子
首先是我们可以使用@TestPropertySource来给出一个不同的配置,在里面声明不同配置。从而让我们无缝连接到不同的数据库。
再之后我们可以准备一个和未来线上数据库一致的测试数据库。这里并不推荐使用嵌入式的内存数据库,这是因为如果使用不同的数据库类型,可能会导致测试过程中出现sql或者函数无法应用的问题。
这里我们还是考虑使用一个相同版本的数据库,可以考虑准备一个配置更低的数据库。
然后之后利用@DataJpaTest,配置数据库回滚能力。
当然如果不是使用的JPA,而是其他的Mybatis等。则也有对应的注解。
比如Spring提供了@JdbcTest。则对应的是使用DataSource。
再之后是Web的测试,对于Spring也支持的很好
我们之前利用@SpringBootTest来将框架集成到了测试之中,做了集成测试
其也有一个简化版本@WebMvcTest
@WebMvcTest(TodoItemResource.class)
public class TodoItemResourceTest { … } |
这样就就只集成TodoItemResource相关的部分了。
除此外,Spring还提供了模拟的Web访问
就是在SpringBootTest注解标记同时声明@AutoConfigureMockMvc,并在类中注入MockMvc对象即可。
@SpringBootTest
@AutoConfigureMockMvc @Transactional public class TodoItemResourceTest { @Autowired private MockMvc mockMvc; … @Test public void should_add_item() throws Exception { String todoItem = “{ ” + “\”content\”: \”foo\”” + “}”; mockMvc.perform(MockMvcRequestBuilders.post(“/todo-items”) .contentType(MediaType.APPLICATION_JSON) .content(todoItem)) .andExpect(status().isCreated()); assertThat(repository.findAll()).anyMatch(item -> item.getContent().equals(“foo”)); } } |
这里的MockMvc并不是真的去进行了一次网络请求,而支持模拟请求去实际调用了代码,只不过调用链路上的各种Filter,拦截器都会被调用。
那么我们看了如何在Spring项目之中进行集成测试。主要是如何做数据库和Web接口的集成测试。
数据库我们说了如何集成不同的配置,以及配置数据回滚
Web测试则是模拟网络请求。进行相关的测试。
Spring的测试相关组件还有很多,还可以去查看其官方文档。