Hot questions for Using Mockito in arraylist

Question:

I need to test a method that is taking a list as an argument. Below is the sample code:

public class C
{
    private int x;
    private String y;
    //getters and setters
}
public class B
{
    public void collectC(List<C> cList)
    {
        for(C c : cList)
        {
            System.out.println("int: " + c.getX() + "String: "+ c.getY());
        }
    }
}

So class B is simply collecting objects of class C and iteration on it using enhanced for loop. Now, I want to test a method of class B. Below is the testing code.

public class BTest
{
    private List<C> cList;
    @Mock private C c;
    @InjectMocks private B b;

    @Before
    public void setUp()
    {
        cList = new ArrayList<>();
        cList.add(c);
        MockitoAnnotations.initMocks(this);
    }

    @Test
    public void testCollectC()
    {
        Mockito.when(c.getX()).thenReturn(5);
        Mockito.when(c.getY()).thenReturn("Hello There");
        b.collectC(cList);
    }
}

So, this is giving me error NullPointerException on System.out.println() one line where I am invoking methods on 'c' object. I then changed the code where I mocked a list and iterator too, so now my code is working fine. But I want to know that what is the problem with above-mentioned code and why it is failing?


Answer:

You have to instance c before you add it to cList otherwise cList contains a null element.

The following test passes:

public class BTest {
    private List<C> cList;
    @Mock
    private C c;

    @InjectMocks
    private B b;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        cList = new ArrayList<>();
        cList.add(c);
    }

    @Test
    public void testCollectC() {
        Mockito.when(c.getX()).thenReturn(5);
        Mockito.when(c.getY()).thenReturn("Hello There");
        b.collectC(cList);
    }
}

Question:

I have a java class.

class Blah{
        public Blah(){

        }
        public String testMe(List<String> s){
            return new String("hello "+s.get(0));
        }


        public String testMeString(String s){
            return new String("hello "+s);
        }


    }

I am unable trying to stub and test the testMe method successfully. Note that I am just trying to understand mocking in java. For example I have tried:

    @Test
    public void testTestMe(){
        Blah blah = spy(new Blah());
        ArrayList<String> l = new ArrayList<String>();
        l.add("oopsie");
        when(blah.testMe(Matchers.any())).thenReturn("intercepted");
        assertEquals("intercepted",blah.testMe(l));

This returns a NullPointerException. I have also tried any(List.class), any(ArrayList.class). I have also tried using anyList() but this gives me an IndexOutOfBounds error. What am I doing wrong? Interestingly, my testMeString works fine. If I do

@Test
    public void testTestMeString(){
        Blah blah = spy(new Blah());
        when(blah.testMeString(any())).thenReturn("intercepted");
        assertEquals("intercepted",blah.testMeString("lala"));
}

the tests pass with any() and any(String.class).


Answer:

With this statement blah.testMe() includes in the when(), it invokes the real method :

when(blah.testMe(Matchers.any())).thenReturn("intercepted");

To avoid that, you should use the doReturn(...).when(...).methodToInvoke() pattern.

doReturn("intercepted").when(blah).testMe(Matchers.any()));

You notice that with this syntax : the blah.testMe() statement is specified nowhere. So that is not called.

Beyond this question I think you don't need any spy to test this method. Spying is a very particular tool in mocking that you use only when you don't have any choice : you need to mock the object under test which is a bad practice and you cannot refactor the actual code.

But here you can just do :

@Test
public void testTestMe(){
    Blah blah = new Blah();
    ArrayList<String> l = new ArrayList<String>();
    l.add("oopsie");
    assertEquals("hello oopsie",blah.testMe(l));
 }

Question:

I'm trying to mock arraylist to verify the add method but I'm getting the message:

FAILED: testInit
Wanted but not invoked:
arrayList.add(<any>);
-> at AsyncRestTemplateAutoConfigurationTest.testInit(AsyncRestTemplateAutoConfigurationTest.java:95)
Actually, there were zero interactions with this mock.

The test class I've used is:

@Test
    public void testInit() throws Exception {

        ArrayList<AsyncClientHttpRequestInterceptor> interceptors = Mockito.mock(ArrayList.class);

        PowerMockito.whenNew(ArrayList.class).withAnyArguments()
                .thenReturn(interceptors);

        Mockito.stub(interceptors.add(Mockito.any())).toReturn(true);
        asyncRestTemplateAutoConfiguration.init();

        Mockito.verify(interceptors).add(Mockito.any());
    }

The actual tested code is:

List<AsyncClientHttpRequestInterceptor> interceptors = new ArrayList<>(interceptors);
    interceptors.add(new TracingAsyncRestTemplateInterceptor(tracer));

I've declared the test class with

@RunWith(PowerMockRunner.class)
@PrepareForTest(AsyncRestTemplateAutoConfiguration.class)

Where AsyncRestTemplateAutoConfigurationis the class, which I'm using to test. Could anyone please tell me what I'm missing?


Answer:

Your unit test should verify public observable behavior which is return values and communication with dependencies (which does not nessessarily imply to test only public methods).

That your production code uses an ArrayList to store your data is an implementation detail which you don't want to test since it may be changed without changing the units general behavior, in which case your unittest should not fail.

Question:

I am testing an enterprise level application using Mockito and JUnit. Here is the code for a method of adding a product to the offline repository class in a product offline-repository-class-test I have:

@Mock
private InitialData initialData;

@InjectMocks
private ProductRepositoryOffline pro;

@Test
public void testPersistProduct() {
    Product product = new Product(0, "", "", "", 0.0, true, "", 0, /*Product type*/null, "", 0, 0);
    ArrayList<Product> productList = new ArrayList<Product>();    
    //productList.add(product);

    Mockito.when(initialData.getProducts()).thenReturn(productList);
    pro.persistProduct(product);
    assertEquals(pro.getProducts().get(0), product);
}

This relies on the following methods in classes:

The method it is testing in the ProductRepositoryOffline:

@Override
public void persistProduct(Product pr) {
    initialData.addProduct(pr);

}

InitialData

private ArrayList<Product> products = new ArrayList<Product>();

public void addProduct(Product product) {
    products.add(product);
}

The question I wish to ask is that in the case of pro.persistProduct(product) unless I have product already added to the ArrayList, isn't persistProduct meant to be adding product to the arrayList without the need for the commented productList.add(product)?


Answer:

Here is what you should be doing:

@Mock
private InitialData initialData;

@InjectMocks
private ProductRepositoryOffline pro;

@Test
public void testPersistProduct() {
    Product product = new Product(0, "", "", "", 0.0, true, "", 0,
        /*Product type*/null, "", 0, 0);
    ArrayList<Product> productList = new ArrayList<Product>();    
    productList.add(product);

    Mockito.when(initialData.getProducts()).thenReturn(productList);
    pro.persistProduct(product);
    assertEquals(pro.getProducts().get(0), product);
    Mockito.verify(initialData).addProduct(product);
}

Because the object initialData is mocked, when it calls the method initialData.addProduct(pr); in your ProductRepositoryOffline, it does nothing. You have to manually add it to the list for checking later in your assertEquals(). To confirm the method was called though, you can use the verify() method to check that the addProduct() was called once on your mock object using the object of product you created. You can see more examples of verify() here

There are other methods to mock void methods, like your usage of addProduct(), to see some examples of those, see this question here

[EDIT] Another variation you could do is to use the doAnswer(), which would look something like this:

Mockito.doAnswer(productList.add(product)).when(initialData).addProduct(product);

I'm not 100% this will work, as I have never used it, but I believe that at the point initialData.addProduct(product); is called, then the product will be added to your product list. That way you wouldn't need to use productList.add(product); Hope that helps a bit!

Question:

I have a method which prepare a list(ArrayList) based on the result of a query.But how can I write test cases to ensure the number and type of records in the list are correct?

Thanks in advance.


Answer:

It depends on the Assertion library that you're using. If you use the JUnit Assert class then you can use assertEquals.

@Test
public void theListIsOk() {
  List<SomeClass> list = sut.getList();
  assertEquals(
    list,
    Arrays.asList(
      new SomeClass(),
      new SomeClass(),
      new SomeClass()
    )
  );
)

The prerequisite is that SomeClass has a proper equals method.

Question:

I am writing a JUnit test case for a method that leads me to another method that is default, it returns a new ArrayList when that default method is called.

Test method

public List getTagDataForImage(String tagType, String imageType) {
        List tagRules = getTagRulesForUpdateOrQueryImage(tagType, imageType);
        List tagData = getTagData(tagRules);
        return tagData;
    }

In the method the stack trace points me to getTagRulesForUpdateOrQueryImage(tagType, imageType); which leads me here

List getTagRulesForUpdateOrQueryImage(String tagType, String imageType) {
        List commonList = tagList.getTagRulesForUpdateOrQueryImage(tagType
                + "," + imageTypes[0]); //This line 
        commonList.addAll(tagList.getTagRulesForUpdateOrQueryImage(tagType
                + "," + imageType));
        return commonList;
    }

definition for tagList.getTagRulesForUpdateOrQueryImage(tagType + "," + imageTypes[0]); defined in another class

List getTagRulesForUpdateOrQueryImage(String tagType) {
        return new ArrayList((List) tagRulesMap.get(tagType));//gives NPE
    }

tagRulesMap is a HashMap that is populated automatically at constructor call

Test case

@InjectMocks 
    TagDataFilter tagDataFilter;
    @Test
        public void testGetTagDataForImage()
        {
            List get=tagDataFilter.getTagDataForImage("QueryImages", "Common");
        }

Stack Trace

java.lang.NullPointerException
    at java.util.ArrayList.<init>(Unknown Source)
    at data.TagList.getTagRulesForUpdateOrQueryImage(TagList.java:107)
    at data.TagDataFilter.getTagRulesForUpdateOrQueryImage(TagDataFilter.java:92)
    at data.TagDataFilter.getTagDataForImage(TagDataFilter.java:73)
    at data.test.TagDataFilterTest.testGetTagDataForImage(TagDataFilterTest.java:54)

I am not able to figure out where I have gone wrong.Please help me solve this problem.


Answer:

Just to provide a answer from the comments:

List getTagRulesForUpdateOrQueryImage(String tagType) {
    return new ArrayList((List) tagRulesMap.get(tagType));//gives NPE
}

the key tagType is not found and because of this it returns null which is then tried to cast which then returns a NullPointerException.

Question:

I have a DAO method which returns a List.

Now I am trying to mock this DAO class in my service layer but when I invoke the DAO method, it is giving me a empty even though I have mocked the DAO method Below is the sample code snippet,

public class ABCTest {

@InjectMocks
ABC abc = new ABC();   

@Mock
private ABCDao dao;

@Before
public void setUp() {
    MockitoAnnotations.initMocks(this);
    dao = Mockito.mock(ABCDao.class);
    Mockito.when(dao.getListFromDB()).thenReturn(Arrays.asList("1","2","3"));
}

@Test
public void testServiceMethod() {
    abc.serviceMethod();  // Inside this method when the DAO method is called, it is giving me an empty list even though I have mocked it above.
}

Any pointers would be helpful


Answer:

  1. Don't use MockitoAnnotations.initMocks(this); use @RunWith(MockitoJunitRunner.class) instead
  2. You are calling dao = Mockito.mock(ABCDao.class) which overrides the dao created by MockitoAnnotations.initMocks(this)
  3. the ABCDao instance inside ABC is now different to the dao member of your test case.

I can only assume that the following would fail:

assertTrue(dao == abc.getDao())

Solution: Remove the following line

dao = Mockito.mock(ABCDao.class);

Question:

I need to re-build some of my methods to return the list of clients. I need that, because in that moment I can't write any unit tests. Here is my method that is responsible for adding clients to DataBase. As you can see values like: name, surname... are fetched from already made client object. CarRentalSQLDatabase

@Override
public void addClient(Client client) throws SQLException {
    preparedStatement = connection.prepareStatement("insert into client" + "(namee, surname, street,houseNumber,city,peselNumber,rentDate, clientNumber)" + "values(?,?,?,?,?,?,?,?)");

    preparedStatement.setString(1, client.getName());
    preparedStatement.setString(2, client.getSurname());
    preparedStatement.setString(3, client.getStreet());
    preparedStatement.setInt(4, client.getHouseNumber());
    preparedStatement.setString(5, client.getCity());
    preparedStatement.setLong(6, client.getPeselNumber());
    preparedStatement.setString(7, client.getRentDate());
    preparedStatement.setInt(8, client.getClientNumber());

    preparedStatement.executeUpdate();
}

Here is my method that is reponsible for displaying all clients on the screen: CarRentalSQLDatabase

@Override
public void populateTableViewClients() throws SQLException {
    String sql = "SELECT * FROM `client`";
    result = statement.executeQuery(sql);

    while (result.next()) {

        String namee = result.getString("namee");
        String surname = result.getString("surname");
        String street = result.getString("street");
        int houseNumber = result.getInt("houseNumber");
        long peselNumber = result.getLong("peselNumber");
        String rentDate = result.getString("rentDate");
        System.out.println("----------------------------");
        System.out.printf("Name:" + namee + "\nSurname:" + surname + "\nStreet:" + street + "\nNumber of house:" + houseNumber + "\nPesel number:" + peselNumber + "\nDate of rental:" + rentDate + "\n");
        System.out.println("----------------------------");
    }
}

I wanted to build something like this:

public List<Client> getAllCustomers() throws SQLException {
    List<Client> listOfClients = new ArrayList<Client>();

    String sql = "SELECT * FROM `client`";
    result = statement.executeQuery(sql);
    //....
    return listOfClients;
}

So how to retrieve data from my DataBase and put it in ArrayList? All that is needed to do test like:

@RunWith(MockitoJUnitRunner.class)
class CarRentalOptionsTest{
CarRentalOptions objUnderTests;

@Test
public void addedCustomerShouldBeSaved(){

    CarRentalStorage storageMock = mock(CarRentalStorage.class);

    objUnderTests = new CarRentalOptions(storageMock);

    Client client = new Client();
    objUnderTests.addClient(client);

    verify(storageMock).addCustomer(client);


    List<Customer> customers = new ArrayList<>();
    customers.add(client);
    when(storageMock.getAllCustomers()).thenReturn(customers);

    Assert.assertTrue(objUnderTests.isCustomerRegistered(customer));
}

Answer:

public List<Client> getAllCustomers() throws SQLException {
    List<Client> listOfClients = new ArrayList<Client>();

    String sql = "SELECT * FROM `client`";
    result = statement.executeQuery(sql);

    while (result.next()) {
        Client client = new Client();
        client.setName(result.getString("namee"));
        client.setSurname(result.getString("surname"));
        // ... and so on
        listOfClients.add(client);
    }

    return listOfClients;
}

Question:

I am going to make method which will analyze big ArrayList. and I want to write test method in JUnit. The size of ArrayList could reach up to couple of millions. I think that it is not good idea to connect to the database and get datas from there for analyzing because a test is not a unit test if it talks to the database. So how should I act correctly in this situation? Or how big datas are analyzed by unit tests generally?

Example:

public void analyze(List<Double> list) {

    double n1, n2, n3;

    for (int i = 3; i < list.size(); i += 3) {

        n1 = list.get(i - 3);
        n2 = list.get(i - 2);
        n3 = list.get(i - 1);

        if (/* Some condition here using n1, n2, n3*/) {
            list.remove(i);
        }
    }
}

@Test
public void analyzeTest() {

    List<Double> list = new ArrayList<Double>();

    // To add 1M data here.

    analyze(list);

    assertEquals(list, expected);

}

Answer:

a test is not a unit test if it talks to the database

You are right.

So how should I act correctly in this situation?

Create an ArrayList object and fill it with data. Then test against this data to assert that your production code behaves as intended. You don't need millions of entries, just the minimum to cover the different cases of analyze().

how big datas are analyzed by unit tests generally?

A good practice is to have multiple levels of test:

  1. Unit tests - verifies the logic of your code, without external resources such as database.
  2. Integration test - verifies that different parts of your system (ex: database, web server, api, etc.) interact correctly with one another.
  3. Performance tests - verifies how your system behaves under stress or with large volumes of data. There are special tools for this (jMeter, gatling).

Question:

According to the official doc: http://site.mockito.org/mockito/docs/current/org/mockito/Mockito.html#CALLS_REAL_METHODS

I wrote the following test:

package tk.puncha.study;

import org.junit.Test;

import java.util.ArrayList;

import static org.mockito.Mockito.*;

public class MockitoTest {

  @Test
  public void test() {
    ArrayList arrayList = mock(ArrayList.class, withSettings().defaultAnswer((CALLS_REAL_METHODS)));
    arrayList.add(new Object());
  }
}

But it crashes with the following exception:

java.lang.NullPointerException
at java.util.ArrayList.ensureExplicitCapacity(ArrayList.java:234)
at java.util.ArrayList.ensureCapacityInternal(ArrayList.java:227)
at java.util.ArrayList.add(ArrayList.java:458)
at tk.puncha.study.MockitoTest.test(MockitoTest.java:16)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:119)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:234)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:74)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

Is it a JUnit bug or I'm wrong?


Answer:

Using a mock with a CALLS_REAL_METHODS default answer is dangerous. Mockito doesn't initialize your mocked object's fields to be realistic (non-null), so real methods will interact with invalid fields and cause errors like the one you're seeing.

Here, ArrayList.ensureExplicitCapacity accesses elementData.length on what must be a null elementData array, leading to your error.

If you used a spy instead, which defaults to CALLS_REAL_METHODS behavior, Mockito would copy over the field values from an actually-constructed object instance you provide. This is generally safer.

(Side note: Outside of toy examples, remember that it's dangerous to mock types you don't own, and that there's almost never a reason to mock simple well-tested library objects like List or its implementations.)