Search code examples
javaspring-bootunit-testingintegration-testingjunit4

Wanted but not invoked..Actually, there were zero interactions with this mock. -Junit4


I know there are tons of similar questions in SO but yet I couldn't find any solution. I have Service class like this. Inside which there is repository method called which returns void. I have created a mock object of that repo class. And injected in the autowired service class but I am getting "Wanted but not invoked..Actually, there were zero interactions with this mock." I have verfied that the repository does getting called.

`    @Service
    public class DecileConfigService
    {
      @Autowired
      private ItemAdditionalInfoRepository itemAdditionalInfoRepository;
      @Transactional
      public Boolean triggerBatchJob(ItemAdditionalInfo existingIAI, ItemAdditionalInfo latestIAI,    DecileConfig existingDC, DecileConfig latestDC, String decileNbr, String decileType,String tenantId, String isBulkUpload) 
      {
      if(!existingIAI.equals(latestIAI))
      {
        System.out.println("Callled ::::::");
        itemAdditionalInfoRepository.updateAllocationStatusByItemAndTenantId(ItemSetupAllocationStatus.PROGRESS.getValue(),Integer.valueOf(decileNbr),lastUpdatedBy,lastUpdatedOn,tenantId);
      }
      } 
    }`

`

Test class :
  @Mock
  ItemAdditionalInfoRepository itemAdditionalInfoRepository;
  @Autowired
  @InjectMocks
  private DecileConfigService decileConfigService;`

  @Test
  public void testRepo() throws Exception
  {
    ItemAdditionalInfo oldIad = DatabricksUtil.createItem();
    oldIad.setVendorNbrFull(12);
    ItemAdditionalInfo newIad = DatabricksUtil.createItem();
    newIad.setVendorNbrFull(13);
    DecileConfig dc = DatabricksUtil.createDecile();
    decileConfigService.triggerBatchJob(oldIad, newIad, dc, dc, "200", "item",  ConstantsUtil.tenantIdValueUS,"false");
    //doNothing().when(itemAdditionalInfoRepository).updateAllocationStatusByDeptAndTenantId(ItemSetupAllocationStatus.PROGRESS.getValue(), Integer.valueOf("200"),newIad.getLastUpdatedBy(),newIad.getLastUpdatedOn(), newIad.getTenantId());      
    verify(itemAdditionalInfoRepository,times(1)).updateAllocationStatusByDeptAndTenantId(ItemSetupAllocationStatus.PROGRESS.getValue(), Integer.valueOf("200"),newIad.getLastUpdatedBy(),newIad.getLastUpdatedOn(), newIad.getTenantId());
  }`

LOG:

Callled :::::: Hibernate:  update item_additional_info  set allocation_status=?, last_updated_by=?, last_updated_on=?  where item_nbr=?  and tenant_id=? 27/May/2023 15:47:34:909 [main]  INFO  com.walmart.sams.services.allocation.configuration.service.TriggerJobService:59 -  info env dev-test url test jobId testid params {decileLevelType=item, autoUpdate=false, decileLevelNumber=200, triggerBy=null, source=Single Edit Trigger, isBulkUpload=false} 27/May/2023 15:47:34:910 [main]  INFO  com.walmart.sams.services.allocation.configuration.service.TriggerJobService:61 -  info env dev-test url test jobName testid params {decileLevelType=item, autoUpdate=false, decileLevelNumber=200, triggerBy=null, source=Single Edit Trigger, isBulkUpload=false} 27/May/2023 15:47:34:989 [main]  ERROR com.walmart.sams.services.allocation.configuration.service.TriggerJobService:34 - Error {} java.lang.IllegalArgumentException: URI is not absolute  at java.net.URL.fromURI(URL.java:692) ~[?:?]    at java.net.URI.toURL(URI.java:1116) ~[?:?]     at org.springframework.http.client.SimpleClientHttpRequestFactory.createRequest(SimpleClientHttpRequestFactory.java:145) ~[spring-web-5.3.27.jar:5.3.27]    at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:98) ~[spring-web-5.3.27.jar:5.3.27]    at com.walmart.platform.txn.springboot.interceptor.SpringBootClientTxnMarkingInterceptor.intercept(SpringBootClientTxnMarkingInterceptor.java:82) ~[strati-af-txn-marking-springboot-client-4.5.9.jar:?]    at org.springframework.http.client.InterceptingClientHttpRequest$InterceptingRequestExecution.execute(InterceptingClientHttpRequest.java:93) ~[spring-web-5.3.27.jar:5.3.27]    at org.springframework.http.client.InterceptingClientHttpRequest.executeInternal(InterceptingClientHttpRequest.java:77) ~[spring-web-5.3.27.jar:5.3.27]     at org.springframework.http.client.AbstractBufferingClientHttpRequest.executeInternal(AbstractBufferingClientHttpRequest.java:48) ~[spring-web-5.3.27.jar:5.3.27]   at org.springframework.http.client.AbstractClientHttpRequest.execute(AbstractClientHttpRequest.java:66) ~[spring-web-5.3.27.jar:5.3.27]     at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:782) ~[spring-web-5.3.27.jar:5.3.27]     at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:717) ~[spring-web-5.3.27.jar:5.3.27]   at org.springframework.web.client.RestTemplate.postForEntity(RestTemplate.java:474) ~[spring-web-5.3.27.jar:5.3.27]     at com.walmart.sams.services.allocation.configuration.service.TriggerJobService.runDatabricksJob(TriggerJobService.java:75) ~[classes/:?]   at com.walmart.sams.services.allocation.configuration.service.TriggerJobService.runBatchJob(TriggerJobService.java:31) [classes/:?]     at com.walmart.sams.services.allocation.configuration.service.DecileConfigService.triggerBatchJob(DecileConfigService.java:496) [classes/:?]    at com.walmart.sams.services.allocation.configuration.service.DecileConfigService$$FastClassBySpringCGLIB$$1122e4d.invoke(<generated>) [classes/:?]     at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) [spring-core-5.3.27.jar:5.3.27]     at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:793) [spring-aop-5.3.27.jar:5.3.27]     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) [spring-aop-5.3.27.jar:5.3.27]     at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) [spring-aop-5.3.27.jar:5.3.27]     at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123) [spring-tx-5.3.27.jar:5.3.27]    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388) [spring-tx-5.3.27.jar:5.3.27]    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119) [spring-tx-5.3.27.jar:5.3.27]     at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) [spring-aop-5.3.27.jar:5.3.27]     at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) [spring-aop-5.3.27.jar:5.3.27]     at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) [spring-aop-5.3.27.jar:5.3.27]   at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) [spring-aop-5.3.27.jar:5.3.27]     at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:763) [spring-aop-5.3.27.jar:5.3.27]     at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:708) [spring-aop-5.3.27.jar:5.3.27]   at com.walmart.sams.services.allocation.configuration.service.DecileConfigService$$EnhancerBySpringCGLIB$$8cc50e9e.triggerBatchJob(<generated>) [classes/:?]    at com.walmart.sams.services.allocation.configuration.test.DatabricksJobServiceTest.testRepo(DatabricksJobServiceTest.java:163) [test-classes/:?]   at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]  at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[?:?]    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]    at java.lang.reflect.Method.invoke(Method.java:566) ~[?:?]  at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:59) [junit-4.13.2.jar:4.13.2]   at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.13.2.jar:4.13.2]    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:56) [junit-4.13.2.jar:4.13.2]     at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit-4.13.2.jar:4.13.2]  at org.springframework.test.context.junit4.statements.RunBeforeTestExecutionCallbacks.evaluate(RunBeforeTestExecutionCallbacks.java:74) [spring-test-5.3.27.jar:5.3.27]     at org.springframework.test.context.junit4.statements.RunAfterTestExecutionCallbacks.evaluate(RunAfterTestExecutionCallbacks.java:84) [spring-test-5.3.27.jar:5.3.27]   at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75) [spring-test-5.3.27.jar:5.3.27]   at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86) [spring-test-5.3.27.jar:5.3.27]     at org.mockito.internal.junit.VerificationCollectorImpl$1.evaluate(VerificationCollectorImpl.java:37) [mockito-core-4.5.1.jar:?]    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84) [spring-test-5.3.27.jar:5.3.27]   at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:366) [junit-4.13.2.jar:4.13.2]  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:251) [spring-test-5.3.27.jar:5.3.27]   at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:97) [spring-test-5.3.27.jar:5.3.27]    at org.junit.runners.ParentRunner$4.run(ParentRunner.java:331) [junit-4.13.2.jar:4.13.2]    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:79) [junit-4.13.2.jar:4.13.2]    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:329) [junit-4.13.2.jar:4.13.2]  at org.junit.runners.ParentRunner.access$100(ParentRunner.java:66) [junit-4.13.2.jar:4.13.2]    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:293) [junit-4.13.2.jar:4.13.2]   at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61) [spring-test-5.3.27.jar:5.3.27]     at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) [spring-test-5.3.27.jar:5.3.27]   at org.junit.runners.ParentRunner$3.evaluate(ParentRunner.java:306) [junit-4.13.2.jar:4.13.2]   at org.junit.runners.ParentRunner.run(ParentRunner.java:413) [junit-4.13.2.jar:4.13.2]  at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:190) [spring-test-5.3.27.jar:5.3.27]    at org.junit.runner.JUnitCore.run(JUnitCore.java:137) [junit-4.13.2.jar:4.13.2]     at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:69) [junit-rt.jar:?]  at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33) [junit-rt.jar:?]   at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:235) [junit-rt.jar:?]    at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:54) [junit-rt.jar:?]

org.mockito.exceptions.base.MockitoAssertionError: There were multiple verification failures: Wanted but not invoked: itemAdditionalInfoRepository.updateAllocationStatusByDeptAndTenantId(
    "In progress", 200, null, null,
    "sams_us" ); at com.walmart.sams.services.allocation.configuration.test.DatabricksJobServiceTest.testRepo(DatabricksJobServiceTest.java:165) Actually, there were zero interactions with this mock. 

Solution

  • You must make a choice : do you want to implement an unit test or an integration test ?

    1. Integration test

    When you use @SpringBootTest annotation, all the Spring beans in you app are instantiated and a Spring ApplicaticationContext is created. You're creating an integration test.

    If you need to manipulate some of your Spring beans in your test, you use @Autowire to get your beans injected:

    @SpringBootTest
    class DecileConfigServiceTest {
    
      @Autowired
      ItemAdditionalInfoRepository itemAdditionalInfoRepository;
    
      @Autowired
      private DecileConfigService decileConfigService;`
    
      @Test
      public void testRepo() throws Exception
      {
        ItemAdditionalInfo oldIad = DatabricksUtil.createItem();
        oldIad.setVendorNbrFull(12);
        ItemAdditionalInfo newIad = DatabricksUtil.createItem();
        newIad.setVendorNbrFull(13);
        DecileConfig dc = DatabricksUtil.createDecile();
        decileConfigService.triggerBatchJob(oldIad, newIad, dc, dc, "200", "item",  ConstantsUtil.tenantIdValueUS,"false");
        //doNothing().when(itemAdditionalInfoRepository).updateAllocationStatusByDeptAndTenantId(ItemSetupAllocationStatus.PROGRESS.getValue(), Integer.valueOf("200"),newIad.getLastUpdatedBy(),newIad.getLastUpdatedOn(), newIad.getTenantId());      
        verify(itemAdditionalInfoRepository,times(1)).updateAllocationStatusByDeptAndTenantId(ItemSetupAllocationStatus.PROGRESS.getValue(), Integer.valueOf("200"),newIad.getLastUpdatedBy(),newIad.getLastUpdatedOn(), newIad.getTenantId());
      }
    }
    

    If you need to mock some of your Spring beans in your integration test, create a configuration class for your test, replace the bean you want to mock in it :

        @Profile("decile-test")
        @Configuration
        public class TestConfiguration {
    
            @Bean
            @Primary
            public ItemAdditionalInfoRepository mockItemAdditionalInfoRepository() {
                return mock(ItemAdditionalInfoRepository.class);
            }
    
        }
    

    And add ActiveProfiles annotation on your test class: @ActiveProfiles(value={"decile-test"})

    Now, your "normal" Spring bean of type "ItemAdditionalInfoRepository" is replaced by a Mock in your application context and can be "autowired".

    1. Unit test

    If you want to test only your "DecileConfigService" class, and not all your SpringBoot app, with all your Spring beans in the aplication context, don't set "@SpringBootTest" on your test class, and use Mockito to inject the mocked objects.

        @RunWith(MockitoJUnitRunner.class) // Junit 4
        // @ExtendWith(MockitoExtension.class) - for JUnit 5
        class DecileConfigServiceTest {
    
          @Mock
          ItemAdditionalInfoRepository itemAdditionalInfoRepository;
    
          @InjectMocks
          private DecileConfigService decileConfigService;`
    
          @Test
          public void testRepo() throws Exception
          {
            ItemAdditionalInfo oldIad = DatabricksUtil.createItem();
            oldIad.setVendorNbrFull(12);
            ItemAdditionalInfo newIad = DatabricksUtil.createItem();
            newIad.setVendorNbrFull(13);
            DecileConfig dc = DatabricksUtil.createDecile();
            decileConfigService.triggerBatchJob(oldIad, newIad, dc, dc, "200", "item",  ConstantsUtil.tenantIdValueUS,"false");
                  //doNothing().when(itemAdditionalInfoRepository).updateAllocationStatusByDeptAndTenantId(ItemSetupAllocationStatus.PROGRESS.getValue(), Integer.valueOf("200"),newIad.getLastUpdatedBy(),newIad.getLastUpdatedOn(), newIad.getTenantId());      
            verify(itemAdditionalInfoRepository,times(1)).updateAllocationStatusByDeptAndTenantId(ItemSetupAllocationStatus.PROGRESS.getValue(), Integer.valueOf("200"),newIad.getLastUpdatedBy(),newIad.getLastUpdatedOn(), newIad.getTenantId());
          }
        }
    

    Considering the content of your test class, I think you're doing unit test. So, don't use @SpringBootTest annotation.