java 如何使用“Spring Data JPA”规范对方法进行单元测试

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/35153760/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-02 23:48:25  来源:igfitidea点击:

how to unit test method using "Spring Data JPA" Specifications

javaspringunit-testingspring-dataspring-data-jpa

提问by Seb

I was playing with org.springframework.data.jpa.domain.Specifications, it's just a basic search :

我在玩 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);
    }
}

And that's actually working fine but I'd like to write unit tests for this method and I can't figure out how to check specifications passed to my articleRepository.findAll()

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

At the moment my unit test looks like :

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

@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);


}

Any idea?

任何的想法?

回答by Tiago Zortéa De Conto

I was having almost the same problems as you had, and I changed my class that contains Specifications to be an object instead of just one class with static methods. This way I can easily mock it, use dependency injection to pass it, and test which methods were called (without using PowerMockito to mock static methods).

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

If you wanna do like I did, I recommend you to test the correctness of specifications with integration tests, and for the rest, just if the right method was called.

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

For example:

例如:

public class CdrSpecs {

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

Then you have an integration test for this method, which will test whether the method is right or not:

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

@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);
}

And now when you wanna unit test other components, you can test like this, for example:

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

@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
}

And of course, inside the Service you can still use the whereand andstatic methods of Specifications class.

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

I hope this can help you.

我希望这可以帮助你。

回答by user2004685

If you are writing Unit Tests then you should probably mock the call to findAll()method of articleRepositoryClass using a mocking framework like Mockitoor PowerMock.

如果您正在编写单元测试,那么您可能应该使用模拟框架(如或 )来模拟findAll()articleRepositoryClass方法的调用。MockitoPowerMock

There is a method verify()using which you can check if the mock is invoked for the particular parameters.

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

For Example, if you are mocking the findAll()method of articleRepositoryClass and want to know if this method is called with particular arguments then you can do something like:

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

Mokito.verify(mymock, Mockito.times(1)).findAll(/* Provide Arguments */);

This will fail the test if mock has not been called for the arguments that you provided.

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

回答by GhostCat

Your problem is that you are doing too many things within that one method. You should have three different methods that work on articleRepository.

你的问题是你在那种方法中做了太多的事情。您应该拥有适用于 articleRepository 的三种不同方法。

Then you can use mocking as the others suggest:

然后你可以像其他人建议的那样使用嘲笑:

  • setup your mocks so that you know which call on articleRepository should be made
  • verify that exactly the expected calls are happening
  • 设置您的模拟,以便您知道应该对 articleRepository 进行哪个调用
  • 验证是否正在发生预期的呼叫

Please note: these three methods should be internal; the main point there is: you can't test this method with ONE call from the outside; as it is doing more than one thing, depending on the input that you provide. Thus you need to create at least one test method for each of the potential paths in your code. And that becomes easier (from a conceptual point of view) when you separate your code into different methods.

请注意:这三个方法应该是内部的;主要的一点是:你不能用一个来自外部的调用来测试这个方法;因为它正在做不止一件事,这取决于您提供的输入。因此,您需要为代码中的每个潜在路径至少创建一个测试方法。当您将代码分成不同的方法时,这会变得更容易(从概念的角度来看)。