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

I played with org.springframework.data.jpa.domain.Specifications, this is just a basic search:

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 it really works fine, but I would like to write unit tests for this method, and I cannot figure out how to check the specifications passed to my article. Repository.findAll ()

Currently my unit test is as follows:

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

+6
source share
3 answers

I had almost the same problems as yours, and I changed my class, which contains the specifications as an object, and not just one class with static methods. That way, I can easily mock it, use dependency injection to pass it on, and check which methods were called (without using PowerMockito to mock static methods).

If you want to do what I did, I recommend that you check the specifications with the integration tests, and for the rest, simply if the correct method was called.

For instance:

 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 checks whether the method is correct 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 want to unit test other components, you can test 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 where and and static methods of the specification class.

Hope this helps you.

+2
source

If you write Unit Tests, you should probably make fun of calling the findAll() method of the findAll() class using a fake framework like Mockito or PowerMock .

There is a verify() method in which you can check if mock is called for specific parameters.

For example, if you are mocking the findAll() method of the findAll() class and want to know if this method is called with specific arguments, you can do something like:

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

This will result in failure to test if mock was not called for the arguments you provided.

+1
source

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

Then you can use the mockery as others say:

  • configure your mocks so that you know what call for the article Repository should be made
  • verify that the expected calls are occurring

Please note: these three methods must be internal; the main thing: you cannot test this method with ONE call from the outside; as it does more than one, depending on the data you enter. Thus, you need to create at least one testing method for each of the potential paths in your code. And it becomes easier (from a conceptual point of view) when you separate your code from different methods.

0
source

All Articles