Mockito defining 'when' invokes the answer

I need to mock a JDBC update statement, and I want to make sure that the update string sent is the correct one. I have a MockConnectionManager that has the following method defined for defining the expected query strings:

public void setUpdateExpectedQuery(String ...expectedQueryStrings) throws SQLException {

    Statement mockStatement = mock(Statement.class);
    when(mockConnection.createStatement()).thenReturn(mockStatement);

    when(mockStatement.executeUpdate(anyString())).then(new Answer<String>() {

        @Override
        public String answer(InvocationOnMock invocationOnMock) throws Throwable {
            String str = Arrays.toString(invocationOnMock.getArguments());
            throw new RuntimeException("Update string is not as expected: " + str);
        }

    });

    for(String expected : expectedQueryStrings) {
        when(mockStatement.executeUpdate(expected)).then(new Answer<Void>() {

            @Override
            public Void answer(InvocationOnMock invocationOnMock) throws Throwable {
                // Do something, just don't throw an exception here.
                return null;
            }

        });
    }
}

I want that if an unexpected query string was encountered an exception will be thrown. The query strings we expect are below as mentioned in the Mockito wiki (http://docs.mockito.googlecode.com/hg/latest/org/mockito/Mockito.html#2).

For some reason, when executing the test and just calling the setUpdateExpectedQuery, an exception is thrown.

MockConnectionManager connectionManager = (MockConnectionManager) context.getBean("connectionManager");

connectionManager.setUpdateExpectedQuery("INSERT WHATEVER INTO WHENEVER");

The same exception as written in the first Answer: java.lang.RuntimeException: Update string is not as expected: [INSERT WHATEVER INTO WHENEVER]

How is this possible? is calling 'when' actually invokes the method? I never saw it happening in other cases...

Jon Skeet
people
quotationmark

I suspect the problem is that your calls within the loop (when you call mockStatement.executeUpdate(expected)) match your earlier mock of when(mockStatement.executeUpdate(anyString())).

Remember that the way the mock knows what you're calling within the when is because you're calling it - imagine translating when(mockStatement.executeUpdate(anyString())) into:

int result = mockStatement.executeUpdate(anyString());
OngoingStubbing<Integer> tmp = when(result);
tmp.then(...);

It may be that you just need to have a single when(...) call. So:

public void setUpdateExpectedQuery(String ...expectedQueryStrings)
    throws SQLException {
    final Set<String> capturedQueries = new HashSet<>
       (Arrays.asList(expectedQueryStrings);

    Statement mockStatement = mock(Statement.class);
    when(mockConnection.createStatement()).thenReturn(mockStatement);

    when(mockStatement.executeUpdate(anyString())).then(new Answer<String>() {    
        @Override
        public String answer(InvocationOnMock invocationOnMock) throws Throwable {
            String query = (String) invocationOnMock.getArguments[0];
            if (capturedQueries.contains(query)) {
                return null;
            }
            throw new RuntimeException("Update string is not as expected: " + query);
        }

    });
}

people

See more on this question at Stackoverflow