如何与其约束验证器一起对方法进行单元测试(哪些应该被删除)?

我的应用程序有一个由CDI应用程序作用域Bean组成的服务层:

@ApplicationScoped
@Transactional
public class PostService {
    @Inject private PostRepository postRepo;
    @Inject private UserRepository userRepo;
    @Inject private SectionRepository sectionRepo;
    @Inject private LoggedInUser loggedInUser;

    public PostDto getPost(@PostExists int id){
        Post p = postRepo.findById(id);
        //create post DTO from p
        return post;
    }

    public void delete(@PostExists int id){
        postRepo.remove(postRepo.findById(id));
    }

    public int newPost(@NotBlank @Max(255) String title,
                       @Max(2000) String body,
                       @SectionExists String sectionName){
        User user = userRepo.getByName(loggedInUser.getUsername());
        Section section = sectionRepo.getByName(sectionName);

        Post post = new Post();
        post.setTitle(title);
        post.setContent(body == null || body.isBlank() ? "" : body);
        post.setAuthor(user);
        post.setSection(section);
        post.setType(TEXT);

        return postRepo.insert(post).getId();
    }

} 

当一个方法被调用时,拦截器(在我的例子中是来自ApacheBVal的BValInterceptor.class)通过检查注释并相应地验证参数来检查该方法契约是否被遵守。

如您所见,有一些可能命中数据库的自定义约束,如@SectionExists@PostExists

public class SectionExistsValidator implements ConstraintValidator<SectionExists, String> {
    @Inject SectionRepository sectionRepo;

    @Override
    public void initialize(SectionExists constraintAnnotation) {}

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return (sectionRepo.getByName(value) != null);
    }
}



public class PostExistsValidator implements ConstraintValidator<PostExists, Integer> {
    @Inject PostRepository postRepo;

    @Override
    public void initialize(PostExists constraintAnnotation) {}

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
        return (postRepo.findById(value) != null);
    }
}

我想做的是对我的业务方法(getpostdeletenewPost)和它的验证器一起进行单元测试。应该模拟可能命中数据库的验证器(或者应该模拟它们的依赖项)。

我如何才能做到这一点?如何才能使注入(和模拟注入)在单元测试中对验证器起作用?

以下是我使用的内容:

  • Tomee 8.0.8
  • 用于Bean验证的Apache BVal JSR 303/JSR380(包括在Tomee中)
  • 用于CDI的Apache OpenWebBeans(包括在Tomee中)
  • JUnit5
  • Mockito

我可以使用OpenEJB's ApplicationComposer或Arquillian运行嵌入式容器。但是,我从未使用过Arquillian。

cdi

最终我选择了this really cool library (cdimock),这正是我所需要的:将推荐答案放在一个定制的cdi作用域中,这样相同的模拟实例就可以注入到测试用例中的其他Bean中。这样的事情也可以通过cdi-unit@Produces @Mock注释来实现(尽管我没有亲自尝试过,因为它只支持Weld)

这是我的测试类的代码:

@RunWithApplicationComposer(mode = ExtensionMode.PER_EACH)
@ExtendWith({MockitoExtension.class, CdiMocking.class})
@MockitoSettings(strictness = LENIENT)
@Classes(cdi = true,
         value={PostService.class},
         cdiInterceptors = BValInterceptor.class,
         cdiStereotypes = CdiMock.class)
public class PostServiceTest {

    @Mock SectionRepository sectionRepository;
    @Mock PostRepository postRepository;
    @Mock UserRepository userRepository;
    @Inject PostService service;   

    @BeforeEach
    void setUp() {}

    @AfterEach
    void tearDown() {}

    @Test
    public void noSectionFoundNewPost(){
        String sectionName = "idontexist";
        when(sectionRepository.getByName(sectionName)).thenReturn(null);
        assertThrows(ConstraintViolationException.class,
                () -> service.newPost("title", "body", sectionName));
    }
}

在代码中,我使用的是OpenEJB的应用程序编写器,但我可以轻松切换到任何嵌入式CDI容器

相关文章