shirafuno
shirafuno

Reputation: 427

Mockito: mocking objects and adding to ArrayList

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)?

Upvotes: 3

Views: 27904

Answers (2)

belka
belka

Reputation: 1530

Here is how I solved a similar problem in Scala.

I have a method with the following prototype:

def myMethod(x: Seq[Int]): Seq[MyStruct]

This method applies to each element in x a function. The result of this function depends on the value of the input. Hence, I would like to mock this method in my tests so that it may represent the real life with a pre-made mapping.

Here is how I mocked my function in my test class:

  when(objMock.myMethod(any())).thenAnswer({
    new Answer[Seq[MyStruct]] {
      override def answer(invocation: InvocationOnMock): Seq[MyStruct] =
        invocation.getArgumentAt[Seq[Int]](0, classOf[Seq[Int]]).map(myPremadeMapping)
    }
  })

With my premade mapping looking like this:

  val idToAudience = Map(
    param1 -> Ans1,
    param2 -> Ans2,
    param3 -> Ans3,
    ...
  )

Upvotes: 0

Draken
Draken

Reputation: 3189

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!

Upvotes: 5

Related Questions