当前位置: 首页 > 知识库问答 >
问题:

在Spring上模拟@Validated注释控制器方法的ConstraintValidator

柯冯浩
2023-03-14

使用Spring Boot 1.3.6. RELEASE,我正在尝试使用JUnit、Mockito和MockMvc对控制器方法进行单元html" target="_blank">测试。我已经构建了一个自定义约束验证器(扩展了ConstraintValidator),它可以自动构建服务。我的目标实体用自定义验证器注释和组进行注释。我的控制器方法签名如下:

@RequestMapping ( value = "api/task/newtask", method = RequestMethod.POST )
public ResponseEntity submitTask ( @Validated ( value = TaskDependenciesDbValidation.class ) 
                                   @RequestBody Task task )
@Override
    public boolean isValid ( Ticket ticket, ConstraintValidatorContext context )
    {
        try
        {
            this.ticketService.verifyTicketExists( ticket.getId() );
            return true;
        } catch ( ResourceNotFoundException e )
        {
            context.disableDefaultConstraintViolation();
            context.buildConstraintViolationWithTemplate( "Ticket with id " + ticket.getId() + " not found" )
                    .addConstraintViolation();
            return false;
        }
    }

在运行时,一切正常。

我想通过完全模拟我的自定义约束验证器,或者只是模拟验证器使用的ticketService,对控制器的submitTask方法进行单元测试。

我试着用Mockito“传统地”(不是同时)模拟服务和验证器。当(…)但我在服务上收到了一个NullPointerException。

测试尝试:

@RunWith ( SpringJUnit4ClassRunner.class )
@SpringApplicationConfiguration ( classes = MyApplication.class )
@ContextConfiguration ( classes = MockServletContext.class )
@WebAppConfiguration
public class TaskControllerTests
{
    @InjectMocks
    TaskController taskController;
    @Mock
    TaskService taskService;
    @Mock
    TicketService ticketService;
    .
    .
    @Before
    public void setup ()
    {
        MockitoAnnotations.initMocks( this );
        mockMvc = standaloneSetup( this.taskController )
            .setControllerAdvice( new RestExceptionHandler() )
            .build();
    }
    .
    .
    @Test
    public void submitTask () throws Exception
    {
        when( taskService.create( any( Task.class ) ) ).thenReturn( this.task );
        doNothing().when( ticketService ).verifyTicketExists( 1L );
        mockMvc.perform( post( "/api/task/newtask" ).content( "..validpayload..") ).andExpect( status().isCreated() );
    }
}

共有3个答案

陈马鲁
2023-03-14

这是一个老问题,但我想我会提供一个对我有用的答案,而且非常简单。不确定这个问题第一次被问到时是否可用。

@RunWith(SpringRunner.class)
@SpringBootTest(classes= {ValidationAutoConfiguration.class})
public class AllowedValuesValidatorTest {
    @Autowired
    private Validator validator;

    @Test
    public void testIsValid() {
        ObjectToBeValidated obj = // create object

        Set<ConstraintViolation<ObjectToBeValidated>> violations = validator.validate(obj);
        boolean violationsFound =
            violations.stream().anyMatch(v -> v.getConstraintDescriptor().getAnnotation().annotationType().equals(
                    NonNullLowercaseLettersOrNumbersOnly.class));
        assertThat(externalIdViolationFound).isTrue();
    }
}

这是验证自动配置。类作为执行重载的测试的配置。这将对ObjectToBeValidated进行所有验证,您可以搜索违规行为,只搜索您正在测试的那个。

孟和怡
2023-03-14

我得到它的工作通过以下这篇文章tomasz_kusmierczyk

我创建的约束验证工厂可以在这里找到,“测试验证器”的创建可以在这里找到。

我的自定义验证器(TicketExistsValidatorUsersExistValidator)在内部使用服务(TicketServiceUserService)。我在测试中创建这些服务的模拟,然后将模拟的服务传递给工厂(工厂随后使用验证器的设置器来设置模拟服务)。

之后,可以使用mockito模拟服务。when()。

为了让注释约束(@NotNull,@Size)在测试中发挥作用,我必须同时使用@Valid和@Validated(CustomGroup.java)对控制器进行注释。

宿文栋
2023-03-14

您可以使用JMockit的@Mocked注释完全模拟验证器:

public class Test {

    @Mocked // mocks the class everywhere
    TaskDependenciesDbConstraintValidator validator;

    @Test
    public void testMethod(){
        new Expectations {{ // expect the validator to be called
            validator.isValid((Ticket) any, (ConstraintValidatorContext) any);
            result = true; // and declare the object to be valid
        }}

        // do your testing here
    }
}

至于你的例子,如果你正在对控制器进行单元测试,为什么要提出整个Spring上下文?因为这就是你要做的

@RunWith ( SpringJUnit4ClassRunner.class )
@SpringApplicationConfiguration ( classes = MyApplication.class )
@ContextConfiguration ( classes = MockServletContext.class )
@WebAppConfiguration

首先也是最重要的一点是,你应该移除这些,然后再试一次。

 类似资料:
  • 我有一个下面的RestController,我试图验证search()方法输入参数映射是否为空。然而,在单元测试期间,当RestController实现ServiceIF接口时,它不会被实例化。若我评论实现接口,那个么控制器被实例化,验证工作正常。我不明白为什么为实现某个接口的类注释@Validated不起作用。 我正在写一个下面的单元测试来验证空地图:

  • 我的一个Spring Boot控制器方法调用静态方法。我想使用powermokito测试控制器。请在下面找到相同的代码。我尝试调用mockMvc时出错。perform()方法@RunWith(PowerMockRunner.class) 静态类。getList()是一个获取java的静态方法。注释行处的lang.NullPointerException(即mockMvc.perform(reque

  • 我的控制器类如下: 我有以下资源服务器配置 最后,我的测试如下所示: 问题是我总是获得401 HTTP状态,并显示消息“unauthorized”,“error\u description”:“访问此资源需要完全身份验证”。 我应该如何为@PreAuthorated注释控制器方法方法编写测试?

  • 我想允许一个域的跨源请求。我的项目使用Spring,所以我想利用新的CORS支持。 我遵循下面的示例https://spring.io/blog/2015/06/08/cors-support-in-spring-framework#disqus_thread并尝试了第一个版本。我的控制器注释如下所示: 如果我理解正确的话,mvc-config是一种替代方法。我也试过了: 对于这两种方法,响应似乎

  • 在Spring Boot(Spring MVC)中,我试图根据本问题中的#4测试表单到控制器的绑定。我在复制魔法帖请求时遇到问题- 然而,我的断言失败了,itemModel。getId()返回null。当spring调用@Controller类上的方法时,我如何像spring那样初始化模型? 更新 我已经更新了以下内容,但它仍然不起作用:

  • 我有一个例子,我有多个cookie值需要用来填充一个Java对象。我希望我的控制器签名看起来像这样: 我已经为Cookie[]创建了一个到Person的转换器,并在我的WebMvcConfigurerAdapter类上使用addFormatters方法注册了它,但是我似乎无法让Spring使用它。 我有另一个工作正常的转换器,它以相同的方式设置。两者之间的唯一区别是工作变量使用@PathParam