在我的应用程序中,我有一个基于给定模式的控制器:
public class Controller {
@Autowired
Mapper mapper;
@Autowired
Service service;
public EntityDto create(EntityDto dto) {
Entity entity = mapper.mapToEntity(dto);
Entity saved = service.save(entity);
return mapper.mapToDto(saved);
}
这样的测试课程是什么好方法?我看到了一些可能性:
- 用Mockito模拟所有内容,并检查从一个模拟中检索到的对象是否传递给另一个模拟
- 在运行春季上下文的情况下进行集成测试
- 跳过控制器的测试,因为它不包含业务逻辑
上面有任何一个可以吗?也许有其他方式?
我假设您正在使用Spring Boot应用程序,您可以在测试包下编写测试类:
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = AppConfig.class)
public class TestController {
@Autowired
Controller controller;
@Test
public void test() {
fail("Not yet implemented");
}
@Test
public void testGroupAlert() throws EntityNotFoundException, Exception {
Entitydto dto = new Entitydto() //Initialize your Entitydto object
controller.create(dto);
}
我最常看到的方法是:
- 创建控制器的开发人员使用模拟。
- QA团队使用春季环境创建集成测试(和工具像黄瓜一样)。
如果要进行出色的测试,唯一的方法是进行单元测试和集成测试。
仅在需要时启动集成测试(例如:Maven可以识别该测试是否是集成的)。
,如果您想要弹性代码,则无法替换任何东西。
做到这一点的好方法是,您猜想使用模拟引擎。(就像摩根托)。
目的是在不相互作用的情况下真正控制类。
但是要当心!如果您在所有层和所有类中都这样做,则可以使用。
如果您正在研究现有代码,请查看已完成的工作并尝试使用它并尽力而为。
记住,一个好的测试不是覆盖最多的测试,它是覆盖最好的测试。
您的控制器没有业务逻辑,但是它接受并产生了一些JSON(或其他数据),这些JSON将由其他服务(即前端)使用。
这就是为什么值得检查API合同的原因。
为此,您可以使用Spring可以合同框架。
用Mockito模拟所有内容,并检查从一个模拟中检索的对象是否传递给另一个模拟的对象
通常,您将逻辑提取到解释其所做工作的方法中。如果该方法使用另一个方法调用的结果,那么我将模拟该方法调用并使其返回固定值。
在运行的春季上下文中进行集成测试
您当然应该进行验证春季上下文的测试(您可能只需要一个)
跳过控制器的测试,因为它不包含业务日志
您的控制器不应包含业务逻辑,而应委派给有效的服务。Spring具有MockMVC,可让您一次测试一个控制器(并在春季上下文中模拟其他豆子)。这为一些有趣且有用的测试提供了许多机会。
-
我更喜欢嘲笑外部系统。我喜欢测试控制器和服务中的逻辑。使用MockMVC i也可以验证HTTP响应。
假设我有三个分层应用程序: Controller -> Service -> Repository (mock this one)
使用MockMVC的示例测试:
@ExtendWith(SpringExtension.class)
@WebMvcTest(controllers = {CategoryController.class})
public class CategoryControllerTest {
@Autowired
private MockMvc mockMvc;
@MockBean
private CategoryRepository repository; // mock database, but validate logic in the service-class
@Test
public void create_new_catgory() throws Exception {
var category = new Category("test");
given(repository.save(any())).willReturn(category);
mockMvc.perform(post("/category")
.content(toJson(new CategoryRequestDto("")))
.contentType(MediaType.APPLICATION_JSON))
.andExpect(status().is2xxSuccessful())
.andExpect(content().json(toJson(category)));
verify(repository, times(1)).save(any());
}
}
测试中的示例更多是集成测试(运行MEM数据库):
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
classes = {ShoprApplication.class, TestConfig.class})
@Transactional
public class ProductControllerTest {
@Autowired TestRestTemplate template;
@Autowired EntityManagerFactory entityManagerFactory;
private TestEntityManager em;
@BeforeEach
public void configure() {
em = new TestEntityManager(entityManagerFactory);
}
@Test
public void createProduct() {
var response = template.postForEntity(
"/product",
new Product("Apples", 15.00, new Category("Fruit"), 6),
Long.class);
assertEquals(HttpStatus.OK, response.getStatusCode());
assertTrue(response.getBody() != null && response.getBody().intValue() > 0L);
var product = em.getEntityManager().find(Product.class, response.getBody());
assertNotNull(product);
assertEquals("Apples", product.getName());
}
1&2
我认为您应该在所有层中使用模仿库的所有课程测试。即使是最琐碎的班级也很适合测试。
然后您应该进行集成测试。
我认为,如果集成测试不需要任何外部系统,则可以一直进行集成测试。唯一的事情可能是时间。我从来没有参加过几分钟等待集成测试的项目。
内存数据库和集成测试库/框架(例如@springboottest)非常适合集成测试。
我认为良好的测试使得做很多重构是可能的。如果您进行重构,则集成测试不会打破太多。JUNIT测试可能会破裂,但我认为这不是问题。您应该始终具有尽可能简单干净的代码和测试。
- 将大部分域逻辑移至模型(您可能需要在DDD上查看一本书)
- 带有单位测试的覆盖域模型。因此,大多数测试都保持在单位级别,并且不需要嘲笑。
- 对于每个功能,只需写一些高级测试 - 他们将检查是否正确完成了序列化,AOP,URL映射,并且一切都从头到脚都起作用。您可以将MockMVC用于此。
您与测试金字塔结束 - 许多快速的单元测试,没有那么多的高级测试,可以一起检查所有测试,没有模拟框架。