Reputation: 10020
It is easy to verify that specific interaction (specific method call) occurred on a mock object in Mockito, and there is verifyZeroInteractions()
for checking that no interactions occurred at all. Suppose I'm testing an interface such as that of a logger, with methods such as info()
, warn()
, error()
etc. In a specific scenario I know one of these methods should be called, but I don't really care which one. Is there a compact way to check that any interaction with the mock object occurred without the need of specifying which exactly method should be called? Or perhaps such a mechanism is not necessary because "the Mockito way" of testing this would be different from what I imagine?
Upvotes: 8
Views: 5680
Reputation: 1583
If you want to check that any interaction occurred with a mock object, you can use the Mockito.mockingDetails()
method and check that the number of invocations is not zero. Of course you could also do more detailed assertions based on the mocking details, but I guess just checking for not zero answers your question.
@ExtendWith(MockitoExtension.class)
public class TestClass {
@Mock
private Logger logger;
@InjectMocks
private Service service;
@Test
public void testMethod_shouldLogMultipleTimes() {
service.testMethod();
assertThat(Mockito.mockingDetails(logger).getInvocations().size()).isNotZero();
}
}
The code example uses assertj
to check that the number of invocations is not zero.
Upvotes: 7
Reputation: 15879
If you can externalise the creation of your logger object from the class under test, there's no reason why you can't write your own test implementation of the Log Interface that will record which methods were exercised and inject it as part of your test setup.
Mock libraries do a lot of good, but sometimes there are corner cases like you have found where they may not cover your needs.
If you write your own implementation for testing like this, and inject it into yourt test class under test, then you can assert on getCount() > 0
public class LoggerTestSupportImpl implements ILogger {
private int count = 0;
@Override
public int getCount() {
return count;
}
@Override
public void info(String message) {
count++;
}
@Override
public void warn(String message) {
count++;
}
}
Upvotes: 2
Reputation: 7937
With log4j, to test the logger I do the following setup:
@Mock private Appender log4jAppender;
private Logger logger;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
Logger root = Logger.getRootLogger();
if (!root.getAllAppenders().hasMoreElements()) {
// No appenders means log4j is not initialized!
BasicConfigurator.configure();
}
logger = Logger.getLogger(MyClassWhichLogs.class);
logger.addAppender(log4jAppender);
}
and then in my test I do the following:
verifyZeroInteractions(log4jAppender);
or
verify(log4jAppender).doAppend(any(LoggingEvent.class);
If you need to test the values logged, you can provide a captor instead:
ArgumentCaptor<LoggingEvent> logCaptor = ArgumentCaptor.forClass(LoggingEvent.class);
verify(log4jAppender).doAppend(logCaptor.capture());
assertTrue(logCaptor.getValue().contains("my text to match");
While this doesn't necessarily answer the generalized question (I don't think what you're looking for exists), it may solve this specific problem for testing logging.
Upvotes: 3