如何将方法及其约束验证器(其中一些应该模拟出来)进行单元测试



我的应用程序有一个服务层,由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
  • Apache BVal for Bean Validation JSR 303/JSR380(包含在TomEE中(
  • Apache OpenWebBeans for CDI(包含在TomEE中(
  • JUnit 5
  • Mockito

我可以使用OpenEJB的ApplicationComposer或Arquillian来运行嵌入式容器。然而,我从未使用过阿奎利安。

最后,我选择了这个非常酷的库(cdimock(,它正是我所需要的:将mock放在自定义CDI范围中,这样就可以在测试用例中的其他bean中注入相同的mock实例。这样的事情也可以通过cdi单元@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的Application Composer,但我可以很容易地切换到任何嵌入式CDI容器

最新更新