3

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

Draken
  • 3,134
  • 13
  • 34
  • 54
shirafuno
  • 387
  • 3
  • 9
  • 24
  • You've only declared what the mock of `initialData` does in the method `getProducts`, which I assume `ProductRepositoryOffline ` calls in it's version of `getProducts()`. What should `initialData.addProduct(pr)` do? At the moment, it does nothing – Draken May 17 '16 at 09:55
  • The problem is that you can't define the Mockito to add an object to a specific list when a method is called, it doesn't work that way. So unless you do `productList.add(product);`, the item of `product ` would never be added to the list. – Draken May 17 '16 at 10:00

2 Answers2

5

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!

Community
  • 1
  • 1
Draken
  • 3,134
  • 13
  • 34
  • 54
0

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,
    ...
  )
Dharman
  • 30,962
  • 25
  • 85
  • 135
belka
  • 1,480
  • 1
  • 18
  • 31