Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/373.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java Spring控制器单元测试最佳实践_Java_Spring_Unit Testing_Mockito - Fatal编程技术网

Java Spring控制器单元测试最佳实践

Java Spring控制器单元测试最佳实践,java,spring,unit-testing,mockito,Java,Spring,Unit Testing,Mockito,在我的应用程序中,我有一个基于给定模式的控制器: 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 mapp

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

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上下文的情况下进行集成测试
  • 跳过控制器的测试,因为它不包含业务逻辑

  • 以上任何一个都可以吗?也许有别的办法

    我假设您使用的是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团队使用Spring上下文(和工具)创建集成测试 喜欢)

    如果您想进行出色的测试,唯一的方法就是进行单元测试和集成测试

    只有在需要时才会启动集成测试(例如:Maven可以确定测试是否为集成测试)

    如果您想要有弹性的代码,那么单元测试不能被任何东西所取代

    这样做的一个好方法是,正如您所猜测的,使用模拟引擎。(像莫基托)

    这样做的目的是以各种可能的方式真正控制类,而不需要其他层的交互

    但要当心!如果在所有层和所有类上都执行此操作,则此操作有效

    如果您正在处理现有代码,请查看已完成的工作,并尝试使用它并尽最大努力


    请记住,一个好的测试并不是覆盖最多的测试,而是覆盖最好的测试。

    您的控制器没有业务逻辑,但它接受并生成一些JSON(或其他数据),这些数据将被其他服务(即前端)使用

    这就是为什么检查API合同是值得的

    为此,您可以使用框架

    用Mockito模拟一切,并检查从一个模拟中检索到的对象是否传递给另一个

    通常,您会将逻辑提取到解释其功能的方法中。如果该方法使用另一个方法调用的结果,那么我将模拟该方法调用并使其返回一个固定值

    在运行Spring上下文的情况下进行集成测试

    您当然应该有一个测试来验证您的spring上下文(您可能只需要一个)

    跳过控制器的测试,因为它不包含业务日志

    您的控制器不应该包含业务逻辑,而是应该委托给执行某些工作的服务。Spring有MockMvc,它允许您一次测试一个控制器(并在Spring上下文中模拟其他bean)。这为一些有趣和有用的测试提供了很多机会

    -

    我更喜欢模拟外部系统。我喜欢测试服务中的控制器和逻辑。使用MockMvc,我还可以验证http响应

    假设我有一个三层应用程序:
    Controller->Service->Repository(模拟这个)

    使用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数据库中运行):

    1和2

    我认为您应该在所有层中使用mocking库测试所有类。即使是最简单的类也可以进行测试

    然后您应该进行集成测试

    我认为集成测试也可以一直运行,如果它们不需要任何外部系统工作的话。唯一能做的就是时间。我还没有参与过这样的项目,因为等待集成测试的时间太长了

    内存中的数据库和集成测试库/框架(例如@SpringBootTest)对于集成测试非常有用

    我认为好的测试可以进行很多重构。如果您进行重构,集成测试不会中断太多。JUnit测试可能会失败,但我认为这不是问题。您应该始终拥有尽可能简单和干净的代码和测试

  • 将大部分域逻辑移到模型中(您可能想查看一本关于DDD的书)
  • 用单元测试覆盖域模型。因此,大多数测试都停留在单元级,不需要模拟
  • 对于每一项功能,只需编写一些高级测试—他们将检查序列化、AOP、URL映射是否正确,以及所有功能是否从头到脚正常工作。您可以使用MockMvc实现这一点

  • 你最终得到了大量的快速单元测试,没有那么多的高级测试一起检查,没有模拟框架。

    但这实际上是一种集成测试,而不是单元测试。如果你真的想进行单元测试,为什么要使用字段注入?其关联性如何?:)我也经常看到这一点,但在IMO中,这种方法与生产代码的耦合太多。在这种情况下,在不破坏测试的情况下进行重构是不可能的,而且这也是单元测试的主要目标之一。使用这种方法,测试将不支持重构,因为每一行代码的更改都会破坏测试。1和2一起。你得到了一些答案,它们都不够好吗?请详细说明这个问题。例如,您的控制器应该具有与选项相关的注释,如GetMapping、RestController等:1。模拟服务,但不模拟映射器(应该有自己的unittest)&2始终有一个测试来验证有效的Spring上下文。3.Spring团队创建MockMvc是为了测试控制器。我建议使用它
    @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());
    }