Verification in mocking

Introduction to Testing in Java

Maria Milusheva

Senior Software Engineer

Motivation: No return values

In the previous lesson and its exercises, we tested by asserting on return values.

  • But what if there are no return values?

  • What if the return values are not important or informative?

Example: saving to a database.

Graphics of a database with files being saved into it

Introduction to Testing in Java

Example: Databases

Database - a digital repository for storing, managing and securing organized collections of data. There are many database providers:

The logos of some popular databases

  • Database servers are durable, secure, reliable, and have unlimited capacity

  • Real databases are too large and complex for unit tests (but may be used in integration tests)

Introduction to Testing in Java

Example: Log messages

Suppose we are processing logs:

public class MessageProcessor {
    private InfoStore infoStore;  // Will store info log messages here
    private ErrorStore errorStore; // Will store error log messages here

    public void saveMessage(String message) {
        if (message.startsWith("[INFO]")) {
            infoStore.save(message);
        }
        if (message.startsWith("[ERROR]")) {
            errorStore.save(message);
        }
    }
}
Introduction to Testing in Java

InfoStore and ErrorStore

Only need basic interfaces for the test to work:

// Blueprints for InfoStore and ErrorStore
// Mocks don't need the mocked classes to be properly implemented
interface InfoStore {
    void save(String message);
}

interface ErrorStore {
    void save(String message);
}
Introduction to Testing in Java

Mockito verify

How can we test a message was saved without creating any databases?

Assert a mock was used:

import static org.mockito.Mockito.verify;

Assert a mock was not used:

import static org.mockito.Mockito.verifyNoInteractions;
Introduction to Testing in Java

Test setup

@Test
void process_savesToInfoStore_whenInfoMessage() {
  InfoStore infoStore = mock(InfoStore.class);
  ErrorStore errorStore = mock(ErrorStore.class);
  MessageProcessor messageProcessor = new MessageProcessor(infoStore, errorStore);

String message = "[INFO] Process started."; messageProcessor.saveMessage(message); // Will use either InfoStore or ErrorStore
// Verify which one of the two databases was used verify(infoStore).save(message); verifyNoInteractions(errorStore); }
Introduction to Testing in Java

Test failure messages

What if the message we create is instead:

String message = "[ERROR] Process failed!"

Then we will see test failures like this:

Wanted but not invoked:
infoStore.save("[ERROR] Process failed!");
Actually, there were zero interactions with this mock.
Introduction to Testing in Java

Further verification tricks

We can verify exactly how many times a log was invoked:

import static org.mockito.Mockito.times;
List<String> messages = new ArrayList<>(); // Create list and add elements
                messages.add("[INFO] Processing data...");
                messages.add("[INFO] Processing data...");
                messages.add("[INFO] Processing data...");

messageProcessor.saveMessageList(messages); // Save the three messages


verify(infoStore, times(3)).save("[INFO] Processing data...");
Introduction to Testing in Java

Let's practice!

Introduction to Testing in Java

Preparing Video For Download...