0

Suppose i want to write unit testcases for this method

@Service
public class OfferServiceImpl implements OfferService {

@Autowired
OfferRepository OfferRepository;

@Override
public List<Offer> getAllOffers() {
    return OfferRepository.findAll();
}

i tried writing by two ways..

first is

@RunWith(MockitoJUnitRunner.class)
public class OfferServiceTest {

@Mock
private OfferRepository offerRepositoryMock;

@InjectMocks
private OfferServiceImpl offerServiceImpl;




    @Test
        public void getAllOffersTest() {
            List<Offer> offerList=new ArrayList<>();
            offerList.add(new Offer("SE",new Date(), 10));
 //line number 47 -->>when(offerRepositoryMock.findAll()).thenReturn(offerList);
            assertNotNull(offerServiceImpl.getAllOffers());
            assertEquals(offerList, offerServiceImpl.getAllOffers());
        }

and 2nd is

@RunWith(SpringRunner.class)
@SpringBootTest(classes = OfferServiceImpl.class) 
public class OfferServiceTest {

private OfferRepository offerRepositoryMock;
private OfferServiceImpl offerServiceImpl;


@Before
public void setUp() {
    offerRepositoryMock = Mockito.mock(OfferRepository.class);
    offerServiceImpl = new OfferServiceImpl();
}


@Test
public void getAllOffersTest() {
    List<Offer> offerList=new ArrayList<>();
    offerList.add(new Offer("SE",new Date(), 10));
    when(offerRepositoryMock.findAll()).thenReturn(offerList);
    assertNotNull(offerServiceImpl.getAllOffers());
    assertEquals(offerList, offerServiceImpl.getAllOffers());
}

Below exception i am getting in both ways tried adding setUp also but getting the same exception

   FAILED: getAllOffersTest
java.lang.NullPointerException
    at com.singh.recruitsystem.service.OfferServiceTest.getAllOffersTest(OfferServiceTest.java:47)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:84)
    at org.testng.internal.Invoker.invokeMethod(Invoker.java:714)
    at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:901)
    at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1231)
    at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:127)
    at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:111)
    at org.testng.TestRunner.privateRun(TestRunner.java:767)
    at org.testng.TestRunner.run(TestRunner.java:617)
    at org.testng.SuiteRunner.runTest(SuiteRunner.java:334)
    at org.testng.SuiteRunner.runSequentially(SuiteRunner.java:329)
    at org.testng.SuiteRunner.privateRun(SuiteRunner.java:291)
    at org.testng.SuiteRunner.run(SuiteRunner.java:240)
    at org.testng.SuiteRunnerWorker.runSuite(SuiteRunnerWorker.java:52)
    at org.testng.SuiteRunnerWorker.run(SuiteRunnerWorker.java:86)
    at org.testng.TestNG.runSuitesSequentially(TestNG.java:1224)
    at org.testng.TestNG.runSuitesLocally(TestNG.java:1149)
    at org.testng.TestNG.run(TestNG.java:1057)
    at org.testng.remote.AbstractRemoteTestNG.run(AbstractRemoteTestNG.java:114)
    at org.testng.remote.RemoteTestNG.initAndRun(RemoteTestNG.java:251)
    at org.testng.remote.RemoteTestNG.main(RemoteTestNG.java:77)

what is difference b/w both ways and how to resolve nullPOinter ecception. i want to test this as a standalone. what should be my correect way of writting unit test cases for this.

Ankit
  • 81
  • 2
  • 10
  • can you show us which line is 47? there is no numbering here. – Sir. Hedgehog Oct 18 '18 at 07:07
  • its `when(offerRepositoryMock.findAll()).thenReturn(offerList);` @Sir. Hedgehog – Ankit Oct 18 '18 at 07:08
  • i would guess its here (`assertNotNull(offerServiceImpl.getAllOffers());`) cause you are getting offers from an object that you didnt put anything in. But you are doing the same thing at `when(offerRepositoryMock.findAll()).thenReturn(offerList);` . you use the findAll at an object that is not instantiated and has nothing in, so thats the cause i believe – Sir. Hedgehog Oct 18 '18 at 07:12
  • btw is the 2nd way actually working? – Sir. Hedgehog Oct 18 '18 at 07:16
  • no, getting the same nullpointer exception. @Sir. Hedgehog – Ankit Oct 18 '18 at 07:20
  • try to call the setUp() at the start of getAllOffersTest(). and tell me what happened – Sir. Hedgehog Oct 18 '18 at 07:22
  • added but still getting the same exception @Sir. Hedgehog – Ankit Oct 18 '18 at 07:24
  • The question needs to be more precise. which approach works and which doesn't and at what point. The exception talks about failure at line 47 while there is no numbering the in examples provided. what is line 47? which approach? – Anand Mattikopp Oct 18 '18 at 07:25
  • 2
    @Sir.Hedgehog In fact I did. Hence the request for the question to be more precise. The problem is question is not updated with those details. It fairly unfair to expect people who are trying to help to go through comments to get the details. – Anand Mattikopp Oct 18 '18 at 07:32

5 Answers5

0

You do not have getter and setter for offerRepository object inside OfferServiceImpl class hence even @InjectMock is not able to set it resulting in a null pointer, I have used reflection to do the same.

   @RunWith(SpringRunner.class)
    @SpringBootTest(classes = OfferServiceImpl.class) 
    public class OfferServiceTest {

    private OfferRepository offerRepository;
    private OfferServiceImpl offerServiceImpl;


    @Test
    public void getAllOffersTest() {
    try {
                Class<?> clazz = Class.forName("com.package.OfferRepository"); //full qualified package name
                Constructor<?> constructor = clazz.getConstructor(); //assuming default or no org constructor for OfferRepository class
                offerRepository = (OfferRepository)constructor.newInstance();
                Field decField = OfferServiceImpl.class.getDeclaredField("OfferRepository");
                decField.setAccessible(true);
                decField.set(OfferServiceImpl, offRepo);
                List<Offer> offerList=new ArrayList<>();
                    offerList.add(new Offer("SE",new Date(), 10));
                    when(offerRepository.findAll()).thenReturn(offerList);
                    assertNotNull(offerServiceImpl.getAllOffers());
                    assertEquals(offerList, offerServiceImpl.getAllOffers());

            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (SecurityException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }

    }
Amit Kumar Lal
  • 5,537
  • 3
  • 19
  • 37
0

For the second, you have a null pointer because offerRepositoryMock is never injected to your service. You never say to spring to create your service.

A to do this, it is to add in your second test @Autowired to your OfferServiceImpl and use the annotation @MockBean (org.springframework.boot.test.mock.mockito.MockBean) to your offerRepositoryMock.

With this change you can delete your setup method

Hope it helps

Sodala
  • 300
  • 1
  • 7
0

Why have you mocked OfferServiceImpl . OfferServiceImpl is the class that contains the method to test. Please don't mock this class. Mock only the OfferRepository class and its method findAll(). If you do so, then the rest should work as is. Code snippet as given below.

public class OfferServiceTest {

@Mock
private OfferRepository offerRepositoryMock;

private OfferServiceImpl offerServiceImpl = new OfferServiceImpl();
//Or use autowired maybe, I am not veru much familiar with Spring

@Test
public void getAllOffersTest() {
List<Offer> offerList=new ArrayList<>();
offerList.add(new Offer("SE",new Date(), 10));
when(offerRepositoryMock.findAll()).thenReturn(offerList);
assertNotNull(offerServiceImpl.getAllOffers());
assertEquals(offerList, offerServiceImpl.getAllOffers());
}
mohd shoaib
  • 151
  • 1
  • 10
  • anotations Mock and Injectmocks are different .. request you to go through the link https://stackoverflow.com/questions/16467685/difference-between-mock-and-injectmocks – Amit Kumar Lal Oct 18 '18 at 08:40
0

@Mock creates a mock. @InjectMocks creates an instance of the class and injects the mocks that are created with the @Mock (or @Spy) annotations into this instance.

Your below test case is failing

@Before
 public void setUp() {
 offerRepositoryMock = Mockito.mock(OfferRepository.class);
 offerServiceImpl = new OfferServiceImpl();
 }


 @Test
 public void getAllOffersTest() {
List<Offer> offerList=new ArrayList<>();
offerList.add(new Offer("SE",new Date(), 10));
when(offerRepositoryMock.findAll()).thenReturn(offerList);
assertNotNull(offerServiceImpl.getAllOffers());
assertEquals(offerList, offerServiceImpl.getAllOffers());
}

Reason being this line , offerServiceImpl = new OfferServiceImpl();

you are creating the Mocks, but not injecting in the service, So your service doesnt have any idea about the Mock injection.

If you want simple solution then , Just have a constructor in your service as

 @Autowired
 OfferServiceImpl(OfferRepository offerRepo) {
    this.OfferRepository = offerRepo;
 }

and in test case change to below , This will solve the problem.

 @Before
 public void setUp() {
 offerRepositoryMock = Mockito.mock(OfferRepository.class);
 offerServiceImpl = new OfferServiceImpl(offerRepositoryMock );
   }
Mohit Sharma
  • 340
  • 2
  • 11
  • what could be the other solutions if i don't want to change anything in service.Just have to write unit cases. @Mohit Sharma – Ankit Oct 18 '18 at 08:40
  • Use Mock and InjectMocks or otherwise find a way where you can inject the mock in your service,I hope you understand the basic problem, Or It can point you to some solution. – Mohit Sharma Oct 18 '18 at 08:50
  • I am using the Mock and InjectMocks in my first method but getting the same issue. you could refer the updated question. @Manish Sharma – Ankit Oct 18 '18 at 08:56
  • There is no reason your first test case should fail,I tried locally it is working,If you can post the stack trace for both the test cases with TestClass and all line numbers. Might be someone can help , without that there is no relevance which test case failing where. – Mohit Sharma Oct 18 '18 at 09:02
  • i have written 2 ways which i am trying for a single service method.you can refer updated question for stack trace @Mohit Sharma – Ankit Oct 18 '18 at 09:14
0

Based on where you're getting the NullPointerException, it is the offerRepositoryMock object that is not getting injected, can you make sure in your imports that the correct import org.mockito.Mock; is imported.

This example works for me:

@RunWith(MockitoJUnitRunner.class)
public class Test {

    @Mock
    private Repository repositoryMock;

    @InjectMocks
    private Service service;

    @org.junit.Test
    public void getAll() {
        List list = new ArrayList();
        list.add("one");

        Mockito.when(repositoryMock.findAll()).thenReturn(list);
        List all = service.getAll();

        Assert.assertNotNull(all);
    }

    public static class Repository {
        public List findAll() {
            return new ArrayList();
        }
    }

    public static class Service {
        private Repository repository;

        public List getAll() {
            return repository.findAll();
        }
    }
}

The difference between the two ways is that if you want to unit test just the single class, the first MockitoJUnitRunner test should be used, and if you want to test the interaction between multiple classes to test a unit of work, the second SpringRunner test should be used, as Spring will construct the objects and autowire them for you. These won't necessarily be mocked objects, but rather the real objects. You can however override the real objects with mocked ones by providing a @Configuration just for the test and provide mocked versions of the objects in this configuration.

Clive
  • 55
  • 7