如何使用"Spring Data JPA"规范进行单元测试方法

Seb*_*Seb 7 java spring unit-testing spring-data spring-data-jpa

我正在玩org.springframework.data.jpa.domain.Specifications,它只是一个基本的搜索:

 public Optional<List<Article>> rechercheArticle(String code, String libelle) {
    List<Article> result = null;

    if(StringUtils.isNotEmpty(code) && StringUtils.isNotEmpty(libelle)){
        result = articleRepository.findAll(Specifications.where(ArticleSpecifications.egaliteCode(code)).and(ArticleSpecifications.egaliteLibelle(libelle)));
    }else{
        if(StringUtils.isNotEmpty(code)){
            result= articleRepository.findAll(Specifications.where(ArticleSpecifications.egaliteCode(code)));
        }else{
            result = articleRepository.findAll(Specifications.where(ArticleSpecifications.egaliteLibelle(libelle)));
        }
    }

    if(result.isEmpty()){
        return Optional.empty();
    }else{
        return Optional.of(result);
    }
}
Run Code Online (Sandbox Code Playgroud)

这实际上工作正常,但我想为这个方法编写单元测试,我无法弄清楚如何检查传递给我的articleRepository.findAll()的规范

目前我的单元测试看起来像:

@Test
public void rechercheArticle_okTousCriteres() throws FacturationServiceException {
    String code = "code";
    String libelle = "libelle";
    List<Article> articles = new ArrayList<>();
    Article a1 = new Article();
    articles.add(a1);
    Mockito.when(articleRepository.findAll(Mockito.any(Specifications.class))).thenReturn(articles);


    Optional<List<Article>> result = articleManager.rechercheArticle(code, libelle);

    Assert.assertTrue(result.isPresent());
    //ArgumentCaptor<Specifications> argument = ArgumentCaptor.forClass(Specifications.class);
    Mockito.verify(articleRepository).findAll(Specifications.where(ArticleSpecifications.egaliteCode(code)).and(ArticleSpecifications.egaliteLibelle(libelle)));
    //argument.getValue().toPredicate(root, query, builder);


}
Run Code Online (Sandbox Code Playgroud)

任何的想法?

Tia*_*nto 7

我遇到了与您几乎相同的问题,我将包含规范的类更改为一个对象,而不仅仅是一个具有静态方法的类。这样我就可以轻松模拟它,使用依赖注入来传递它,并测试调用了哪些方法(不使用 PowerMockito 模拟静态方法)。

如果你想像我一样做,我建议你用集成测试来测试规范的正确性,其余的,只要调用了正确的方法。

例如:

public class CdrSpecs {

public Specification<Cdr> calledBetween(LocalDateTime start, LocalDateTime end) {
    return (root, query, cb) -> cb.between(root.get(Cdr_.callDate), start, end);
}
}
Run Code Online (Sandbox Code Playgroud)

然后你对这个方法有一个集成测试,它会测试这个方法是否正确:

@RunWith(SpringRunner.class)
@DataJpaTest
@Sql("/cdr-test-data.sql")
public class CdrIntegrationTest {

@Autowired
private CdrRepository cdrRepository;

private CdrSpecs specs = new CdrSpecs();

@Test
public void findByPeriod() throws Exception {
    LocalDateTime today = LocalDateTime.now();
    LocalDateTime firstDayOfMonth = today.with(TemporalAdjusters.firstDayOfMonth());
    LocalDateTime lastDayOfMonth = today.with(TemporalAdjusters.lastDayOfMonth());
    List<Cdr> cdrList = cdrRepository.findAll(specs.calledBetween(firstDayOfMonth, lastDayOfMonth));
    assertThat(cdrList).isNotEmpty().hasSize(2);
}
Run Code Online (Sandbox Code Playgroud)

现在当你想对其他组件进行单元测试时,你可以这样测试,例如:

@RunWith(JUnit4.class)
public class CdrSearchServiceTest {

@Mock
private CdrSpecs specs;
@Mock
private CdrRepository repo;

private CdrSearchService searchService;

@Before
public void setUp() throws Exception {
    initMocks(this);
    searchService = new CdrSearchService(repo, specs);
}

@Test
public void testSearch() throws Exception {

    // some code here that interact with searchService

    verify(specs).calledBetween(any(LocalDateTime.class), any(LocalDateTime.class));
   // and you can verify any other method of specs that should have been called
}
Run Code Online (Sandbox Code Playgroud)

当然,里面的服务,你仍然可以使用,其中规范类的静态方法。

我希望这可以帮助你。


use*_*685 4

如果您正在编写单元测试,那么您可能应该使用诸如或 之类的模拟框架来模拟对 ClassfindAll()方法的调用。articleRepositoryMockitoPowerMock

有一种方法verify()可以用来检查是否为特定参数调用了模拟。

例如,如果您正在模拟ClassfindAll()的方法articleRepository并想知道是否使用特定参数调用此方法,那么您可以执行以下操作:

Mokito.verify(mymock, Mockito.times(1)).findAll(/* Provide Arguments */);
Run Code Online (Sandbox Code Playgroud)

如果没有为您提供的参数调用模拟,则测试将失败。

  • 这就是将东西放入不同方法中的要点!不要将规范作为文字放置。相反:编写一个生成规范的方法。测试该方法。然后模拟该方法的结果;只需检查您的方法是否**应用**该规范生成的结果即可。这就是单元测试的整体思想:你必须**解耦**事物。如果你想检查某个东西是否以某种方式使用;好吧,那么这个东西就必须是“从外面”来的;并且您可以提供“模拟”来控制内部行为。 (3认同)
  • 我不想检查它是否只执行一次我想确定使用了哪些规范:Specifications.where(ArticleSpecifications.egaliteCode(code)).and(ArticleSpecifications.egaliteLibelle(libelle))或Specifications.where( ArticleSpecifications.egaliteCode(code)) 或Specifications.where(ArticleSpecifications.egaliteLibelle(libelle)) (2认同)
  • 如上所述,我同意OP的观点。这个答案是可以的,因为它建议使用模拟,OP已将其更新到问题中。然而问题仍然存在;如何断言由被测方法创建并传递给模拟的规范符合预期。我喜欢规范,但是一旦构建出来,它们就变得令人沮丧地不透明。对于这个问题@Seb 的任何解决方案都将是令人惊奇的。 (2认同)