Jaykumar
Jaykumar

Reputation: 155

Testing Interface methods using Mockito

I am writing a JUnit test case for a method which internally invokes another method through interface. I am using Mockito to mock the interface but for some reason it gives me NPE. I debugged through but wasn't able to get any clue to fix it. getAllVendors() method throws exception which comes through an Interface.

MUT

public void prepare() throws AccountServiceException, ManagerException {
vendors = getVendorManager().getAllVendors();

microsites = new ArrayList<VendorMicrositeTO>();
microsites.add( new VendorMicrositeTO( "http://www.docusign.com", "docuSign" ) );

clientUser = createClientUserObject();

}

JUnit

@Test
public void testPrepare() throws Exception {
    AccountAction accountAction = new AccountAction();
    Map<String, Object> actionMap = new HashMap<>();
    actionMap.put("application", "ESignatureIntegrationAction");

    ActionContext.setContext(new ActionContext(actionMap));
    String beanName = Constants.VENDOR_MANAGER_SPRING_BEAN;
    PowerMockito.mockStatic(AppContext.class);
    PowerMockito.when(AppContext.containsBean( beanName )).thenReturn( true );

    IVendorDto iVendorDto = new VendorDto();
    iVendorDto.setActive(true);
    iVendorDto.setCreatedBy("9/15/2016");
    iVendorDto.setName("CorpESignClientUser");
    iVendorDto.setCreatedBy("SYSTEM");

    List<IVendorDto> vendorList = new ArrayList<>();
    vendorList.add(iVendorDto);

    IVendorManager iManager = Mockito.mock((IVendorManager.class));
    Mockito.when(iManager.getAllVendors()).thenReturn(vendorList);

    accountAction.setVendors(vendorList);
    accountAction.prepare();
}

Stack trace

java.lang.NullPointerException
at com.mercuryinsurance.esignature.ui.webapp.action.AccountAction.prepare(AccountAction.java:65)
at test.com.mercuryinsurance.esignature.ui.webapp.action.TestAccountAction.testPrepare(TestAccountAction.java:58)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:606)
at org.junit.internal.runners.TestMethod.invoke(TestMethod.java:66)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:310)
at org.junit.internal.runners.MethodRoadie$2.run(MethodRoadie.java:86)
at org.junit.internal.runners.MethodRoadie.runBeforesThenTestThenAfters(MethodRoadie.java:94)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.executeTest(PowerMockJUnit44RunnerDelegateImpl.java:294)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTestInSuper(PowerMockJUnit47RunnerDelegateImpl.java:127)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit47RunnerDelegateImpl$PowerMockJUnit47MethodRunner.executeTest(PowerMockJUnit47RunnerDelegateImpl.java:82)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$PowerMockJUnit44MethodRunner.runBeforesThenTestThenAfters(PowerMockJUnit44RunnerDelegateImpl.java:282)
at org.junit.internal.runners.MethodRoadie.runTest(MethodRoadie.java:84)
at org.junit.internal.runners.MethodRoadie.run(MethodRoadie.java:49)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.invokeTestMethod(PowerMockJUnit44RunnerDelegateImpl.java:207)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.runMethods(PowerMockJUnit44RunnerDelegateImpl.java:146)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl$1.run(PowerMockJUnit44RunnerDelegateImpl.java:120)
at org.junit.internal.runners.ClassRoadie.runUnprotected(ClassRoadie.java:34)
at org.junit.internal.runners.ClassRoadie.runProtected(ClassRoadie.java:44)
at org.powermock.modules.junit4.internal.impl.PowerMockJUnit44RunnerDelegateImpl.run(PowerMockJUnit44RunnerDelegateImpl.java:122)
at org.powermock.modules.junit4.common.internal.impl.JUnit4TestSuiteChunkerImpl.run(JUnit4TestSuiteChunkerImpl.java:106)
at org.powermock.modules.junit4.common.internal.impl.AbstractCommonPowerMockRunner.run(AbstractCommonPowerMockRunner.java:53)
at org.powermock.modules.junit4.PowerMockRunner.run(PowerMockRunner.java:59)
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:50)
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

Thanks, in advance

Upvotes: 4

Views: 15428

Answers (2)

Peter Palka
Peter Palka

Reputation: 49

So yeah, I have been tackling a very similar issue.

This usecase is a logical outcome of the application of the SOLID principles within an IoT based application. If you decouple the layers of your application using interfaces and are testing an innner layer you are bound to come across testing an interface whose implementations have more interface dependencies.

You can achieve this goal using two testing angles combined

  1. Use the Parameterized JUnit runner to launch the one set of unit tests for all of the implementations.
  2. Then internally during each run initialize the mocked dependencies using Mockito

For more information on Parameterized testing (that's where i originally found it) be sure to visit this post. The manual initialization of mockito was something i found here.

All and all the resulting code looks like this:

 @RunWith(Parameterized.class)
 public class YourInterfaceTest {

   @Mock
   private ImplementationDependency sneakyBreakyNpeAvoided;

   @InjectMocks
   private YourInterfaceToTest iface;
   
   // constructor is used by the Parameterized runner to provide impelementations
   public YourInterfaceTest (YourInterfaceToTest ifaceToTest) {
       this.iface = ifaceToTest;
   }

   // this method is called always before running tests so a good time to inject anything
   @Before
   public void init() {
      MockitoAnnotations.initMocks(this);
      Mockito.when(sneakyBreakyNpeAvoided.returnTrue()).thenReturn(true);
   }

   @Test(expected = IllegalArgumentException.class)
   public void doSomething_nullParameter_throwsIllegalArgumentException() {
      Assert.fail(); // tests here :)
   }

   @Parameterized.Parameters
   public static Collection<YourInterfaceToTest > provideImplementations() {
      // change to Arrays.asList when multiple implementations are available
      return Collections.singletonList(new YourInterfaceImpl());
   }
}

Hope I understood the OP's issue well.

Upvotes: 1

A.Bashkin
A.Bashkin

Reputation: 11

Seems you forgot to add a line in your test like:

accountAction.setVendorManager(iManager);

Upvotes: 1

Related Questions