弹簧控制器单元测试最佳实践



在我的应用程序中,我有一个基于给定模式的控制器:

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);
}

这样的测试课程是什么好方法?我看到了一些可能性:

  1. 用Mockito模拟所有内容,并检查从一个模拟中检索到的对象是否传递给另一个模拟
  2. 在运行春季上下文的情况下进行集成测试
  3. 跳过控制器的测试,因为它不包含业务逻辑

上面有任何一个可以吗?也许有其他方式?

我假设您正在使用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测试可能会破裂,但我认为这不是问题。您应该始终具有尽可能简单干净的代码和测试。

  1. 将大部分域逻辑移至模型(您可能需要在DDD上查看一本书)
  2. 带有单位测试的覆盖域模型。因此,大多数测试都保持在单位级别,并且不需要嘲笑。
  3. 对于每个功能,只需写一些高级测试 - 他们将检查是否正确完成了序列化,AOP,URL映射,并且一切都从头到脚都起作用。您可以将MockMVC用于此。

您与测试金字塔结束 - 许多快速的单元测试,没有那么多的高级测试,可以一起检查所有测试,没有模拟框架。

相关内容

  • 没有找到相关文章

最新更新