1

Whenever I am running the following code I am getting error:

Wanted but not invoked:
dynamoDBWriter.addItemsToDynamoTable(
    <any>,
    <any>
);
-> at DynamoDBWriterTest.testAllItemsAdded(DynamoDBWriterTest.java:32)

However, there were other interactions with this mock:
dynamoDBWriter.write(
    [{ Item: {} }, { Item: {} }, { Item: {} }, { Item: {} }, { Item: {} }]
);
-> at DynamoDBWriterTest.testAllItemsAdded(DynamoDBWriterTest.java:31)

Following is my code :

public class DynamoDBWriterTest {

    DynamoDBWriter dynamoDbWriter;

    @Before
    public void setup() {
        dynamoDbWriter = mock(DynamoDBWriter.class);
    }

    @Test
    public void testAllItemsAdded() {
        List<Item> items = new ArrayList<>();
        for (int index = 0; index < 5; index++) {
            items.add(new Item());
        }
        dynamoDbWriter.write(items);
        verify(dynamoDbWriter, times(5)).addItemsToDynamoTable(any(), any());
    }
}

Code from DynamoDBWriter :

public void write(List<Item> items) {
        // Initialize the rate limiter to allow capacity units / sec
        // Since we know that the Item we are putting consumes 1 unit throughput.
        RateLimiter rateLimiter = RateLimiter.create(1);

        // Track how much throughput we consume on each put operation
        for (Item item: items) {
            // Let the rate limiter wait until our desired throughput "recharges"
            rateLimiter.acquire();
            addItemsToDynamoTable(table, item);
        }
    }

    protected void addItemsToDynamoTable(Table table, Item item) {
        try {
            table.putItem(item);
        } catch (RuntimeException e) {
            logger.fatal("dynamoDB table.putItem threw exception for:" + tableName, e);
            throw e;
        }
    }

Thanks for the help. I am also adding the actual "corrected / working" code I used :

@Before
    public void setup() {
        dynamoDbWriter = spy(DynamoDBWriter....);
        doNothing().when(dynamoDbWriter).addItemsToDynamoTable(any(), any());
    }

    // Method makes sure that irrespective of the throughput, all the items are added to dynamoDB
    @Test
    public void testAllItemsAdded() {
        List<Item> items = new ArrayList<>();
        for (int index = 0; index < 5; index++) {
            items.add(new Item());
        }
        dynamoDbWriter.write(items);
        verify(dynamoDbWriter, times(5)).addItemsToDynamoTable(any(), any());
    }
Stefan Birkner
  • 24,059
  • 12
  • 57
  • 72
Amber
  • 1,229
  • 9
  • 29

2 Answers2

1

When you create a mock DynamoDBWriter, by default it overrides all methods with do-nothing stubs. When you call write, Mockito's replacement implementation allows for verification that write is called but not any calls that the write implementation makes. That implementation is never invoked.

The real problem, of course, is that you're mocking the class you're testing. Even with the technical workarounds, it would be very easy to test the mocking framework rather than mocking your class under test. In general, reserve mocking for your collaborators of your system under test, not your system under test itself. (See also JB Nizet's analogy from his answer here.)


That said, if absolutely necessary, you can use a spy of a real instance and selectively override methods as needed, or more dangerously you can use thenCallRealMethod on a mock:

when(dynamoDbWriter.write(any())).thenCallRealMethod();

For the differences between the two, and why thenCallRealMethod is so dangerous, see this SO question.

Community
  • 1
  • 1
Jeff Bowman
  • 90,959
  • 16
  • 217
  • 251
1

You are mocking DynamoDBWriter, this means that calls to it are not really called (they are called, but the internal code in them is not executed, it's just a mock). so the error you are getting here is because only write is executed, not addItemsToDynamoTable

You are mocking the wrong object here- if you want to test DynamoDBWriter, you shouldn't mock it, you should mock the peripheral objects used by it.

In this case, I'd mock table, verify that table.put is executed 5 times (not sure from the code how you construct DynamoDBWriter and pass table to it, but I guess you can pass the mocked table to it)

Nir Levy
  • 12,750
  • 3
  • 21
  • 38