1

At first I want to sorry for my english.

I started to make some unit tests (i've never done this before, i'm a new guy in programming).

I have to test simple adding product to database (DynamoDB) method using mockito.verify but I have

"Wanted but not invoked. Actually, there were zero interactions with this mock." 

Error and I don't know what to do.

This is my method code (in KitchenService class):

public Product addProduct(Product content) {

    ObjectMapper objectMapper = new ObjectMapper();

    String mediaJSON = null;
    String authorJSON = null;
    String productKindsJSON = null;
    try {
        mediaJSON = objectMapper.writeValueAsString(content.getMedia());
        authorJSON = objectMapper.writeValueAsString(content.getAuthor());
        productKindsJSON = objectMapper.writeValueAsString(content.getProductKinds());
    } catch (JsonProcessingException e) {
        logger.log(e.getMessage());
    }


    Item item = new Item()
            .withPrimaryKey("id", UUID.randomUUID().toString())
            .with("name", content.getName())
            .with("calories", content.getCalories())
            .with("fat", content.getFat())
            .with("carbo", content.getCarbo())
            .with("protein", content.getProtein())
            .with("productKinds", productKindsJSON)
            .with("author", authorJSON)
            .with("media", mediaJSON)
            .with("approved", content.getApproved());


    Item save = databaseController.saveProduct(PRODUCT_TABLE, item);
    logger.log(save + " created");



    return content;

}

And this is test code:

@Test
public void addProduct() throws Exception {


    KitchenService instance = mock(KitchenService.class);


    Product expectedProduct = new Product();
    expectedProduct.setName("kaszanka");
    expectedProduct.setCalories(1000);
    expectedProduct.setFat(40.00);
    expectedProduct.setCarbo(20.00);
    expectedProduct.setProtein(40.00);
    expectedProduct.setProductKinds(Collections.singletonList(ProductKind.MEAT));
    expectedProduct.setApproved(false);
    Author expectedAuthor = new Author();
    expectedAuthor.setId("testID");
    expectedAuthor.setName("Endrju Golota");
    expectedProduct.setAuthor(expectedAuthor);
    Media expectedMedia = new Media();
    expectedMedia.setMediaType(MediaType.IMAGE);
    expectedMedia.setName("dupajasia");
    expectedMedia.setUrl("http://blabla.pl");
    expectedProduct.setMedia(expectedMedia);

    verify(instance, times(1)).addProduct(expectedProduct);
}

This is what I got after test:

Wanted but not invoked:
kitchenService.addProduct(
    model.kitchen.Product@a0136253
);
-> at     service.kitchen.KitchenServiceTest.addProduct(KitchenServiceTest.java:80)
Actually, there were zero interactions with this mock.

Can someone tell me what im doing wrong?

Avishek Bhattacharya
  • 6,534
  • 3
  • 34
  • 53
D.Zet
  • 753
  • 3
  • 11
  • 32
  • 2
    you should not be mocking the class under test for sure. If there is something to mock here then that is the ObjectMapper – Maciej Kowalski Sep 27 '17 at 14:32
  • So how can i use verify and call the method without kitchenservice instance? Examples of correct verifications: verify(mock).someMethod(); verify(mock, times(10)).someMethod(); verify(mock, atLeastOnce()).someMethod(); – D.Zet Sep 27 '17 at 14:41
  • Your test makes no sense. You're mocking what you're testing. So you're not executing any of your code. Only Mockito's code. It makes no sense to create a fake thing, call the fake thing methods, and verify that you have called the fake thing methods. The real code is never executed is all you're doing is using a fake thing. Mocking is useful when you want to test A, and A depends on B, and you use a fake B to test A. Read https://stackoverflow.com/a/28783849/571407 for a detailed explanation. – JB Nizet Sep 27 '17 at 14:49
  • You never do anything with `instance`, so there cannot be any calls to it. Also check https://stackoverflow.com/q/74027324/112968 for an in-depth discussion of common problems when using mocks – knittl May 01 '23 at 16:30

3 Answers3

0

What you should mock and verify is the databaseController dependency:

@Test
public void addProduct() throws Exception {

    KitchenService instance = new KitchenService(); // you should create the class under test

    DatabaseController controllerMock = mock(DatabaseController.class); // mock the controller

    instance.setController(controller); // inject the mock

    ...

    // Act
    instance.addProduct(expectedProduct);

    // Assert
    verify(controller).saveProduct(Mockito.eq(PRODUCT_TABLE), Mockito.any(Item.class));

}

You should verify that the database is called within the service.. checking that it was invoked with any Item object should be enough.

Maciej Kowalski
  • 25,605
  • 12
  • 54
  • 63
0

Mocking is a tool that you only use for dependencies of the class that is being tested. It appears that your test does not care about the Author, Media, and Product objects, these are just dependencies of the method you want to test; mock them.

Organization will greatly help your test; do something like this:

public class TestKitchenService
{
    private static String VALUE_PRODUCT_NAME = "VALUE_PRODUCT_NAME";
    ... use constants for other values as well.  The value of the constant does not matter.

    @InjectMocks
    private KitchenService classToTest;

    private InOrder inOrder;

    @Mock
    private Author mockAuthor;

    @Mock
    private DatabaseController mockDatabaseController;

    @Mock
    private Logger mockLogger;

    @Mock
    private Media mockMedia;

    @Mock
    private Product mockProduct;

    @After
    public void afterTest()
    {
        inOrder.verifyNoMoreInteractions();

        verifyNoMoreInteractions(mockAuthor);
        verifyNoMoreInteractions(mockDatabaseController);
        verifyNoMoreInteractions(mockLogger);
        verifyNoMoreInteractions(mockMedia);
        verifyNoMoreInteractions(mockProduct);
    }

    @Before
    public void beforeTest()
    {
        MockitoAnnotations.initMocks(this);

        doReturn(mockAuthor).when(mockProduct).getAuthor();
        doReturn(mockMedia).when(mockProduct).getMedia();
        doReturn(VALUE_PRODUCT_NAME).when(mockProduct).getName();
        doReturn(Collections.singletonList(ProductKind.MEAT)).when(mockProduct).getProductKinds();

        ... doReturns for the other product values.

        inOrder = inOrder(
            mockAuthor,
            mockDatabaseController,
            mockLogger,
            mockMedia,
            mockProduct);

        ReflectionTestUtils.setField(
            classToTest,
            "databaseController",
            mockDatabaseController);

        ReflectionTestUtils.setField(
            classToTest,
            "logger",
            mockLogger);
    }

    @Test
    public void addProduct_success()
    {
        final Product actualResult;


        actualResult = classToTest.addProduct(mockProduct);


        assertEquals(
            mockProduct,
            actualResult);

        inOrder.verify(mockProduct).getMedia();

        inOrder.verify(mockProduct).getAuthor();

        inOrder.verify(mockProduct).getProductKinds();

        inOrder.verify(mockProduct).getName();

        ... inOrder.verify for the other product values.

        inOrder.verify(mockDatabaseController).saveProduct(
            eq(PRODUCT_TABLE),
            any(Item.class));
    }
}
DwB
  • 37,124
  • 11
  • 56
  • 82
0

The only things that should be mocked -- if anything -- are the ObjectMapper and databaseController. One only mocks collaborator objects, and almost never the system/class under test (very rare cases exist for "spying" on the SUT). And depending on what the ObjectMapper is and how transparent it's operation is, you may not really want to even mock that. Furthermore, as your implementation code is written instantiating the ObjectMapper by directly calling a constructor, you can't even mock it.

While I love the use of Mockito and mock objects, sometimes it is worthwhile to simply test with as many real objects as possible. This is especially true when your collaborators are simple, straightforward, have no side effects, and don't require complex initialization or setup. Only use mocks when it simplifies the test setup or verification.

Kevin Welker
  • 7,719
  • 1
  • 40
  • 56