如何与其约束验证器一起对方法进行单元测试(哪些应该被删除)?
我的应用程序有一个由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);
}
}
我想做的是对我的业务方法(getpost
、delete
、newPost
)和它的验证器一起进行单元测试。应该模拟可能命中数据库的验证器(或者应该模拟它们的依赖项)。
我如何才能做到这一点?如何才能使注入(和模拟注入)在单元测试中对验证器起作用?
以下是我使用的内容:
- 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容器
相关文章