Michał Kosmulski
Michał Kosmulski

Reputation: 10020

Is it possible to verify arbitrary interaction using Mockito in a compact way?

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

Answers (3)

maxeh
maxeh

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

Brad
Brad

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

Kevin Welker
Kevin Welker

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

Related Questions