简体   繁体   中英

Getting nullPointer in unit testing of spring boot service

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.

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

    }

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

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

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

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM