Introduction to Testing in Java
Maria Milusheva
Senior Software Engineer
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.
Database - a digital repository for storing, managing and securing organized collections of data. There are many database providers:
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)
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);
}
}
}
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);
}
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;
@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); }
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.
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