Mocks: mock, when, thenReturn
PostService depends on a repository and a notifier. In a real application these are a database and a mail server. A unit test should not need either: it would be slow, fragile and would send real emails. So far we replaced the dependencies by hand — an in-memory repository and a lambda. Mockito creates such stand-ins automatically.
A mock is an object that implements the interface but does nothing by itself. You tell it what to return for which call (this is called stubbing), and afterwards you can ask what calls it received.
Adding Mockito
Add to dependencies in pom.xml:
<!-- pom.xml -->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>5.24.0</version>
<scope>test</scope>
</dependency>mockito-junit-jupiter brings mockito-core and the JUnit integration. On Java 21 and newer Mockito prints a warning at the first test:
Mockito is currently self-attaching to enable the inline-mock-maker. This will no longer work in future releases of the JDK. Please add Mockito as an agent to your build as described in Mockito's documentation: ...
WARNING: A Java agent has been loaded dynamically (...\byte-buddy-agent-1.17.7.jar)Mockito changes classes in memory through a Java agent, and future JDKs will forbid loading an agent on the fly. The Mockito documentation recommends passing the agent to the test JVM explicitly:
<!-- pom.xml -->
<properties>
<!-- ... -->
<argLine></argLine>
</properties>
<build>
<plugins>
<plugin>
<artifactId>maven-dependency-plugin</artifactId>
<version>3.11.0</version>
<executions>
<execution>
<goals>
<goal>properties</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>3.6.0</version>
<configuration>
<argLine>@{argLine} -javaagent:${org.mockito:mockito-core:jar} -Xshare:off</argLine>
</configuration>
</plugin>
</plugins>
</build>The properties goal creates a property with the path to each dependency's jar, so ${org.mockito:mockito-core:jar} becomes the path to Mockito. @{argLine} keeps arguments that other plugins add (we need this for coverage later); the empty argLine property makes it valid when there are none. -Xshare:off removes one more JVM warning about class sharing. After this, the warnings are gone.
The first mock
// src/test/java/com/example/blog/PostServiceMockTest.java
package com.example.blog;
import org.junit.jupiter.api.Test;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
class PostServiceMockTest {
@Test
void findReturnsPostFromRepository() {
PostRepository repository = mock(PostRepository.class);
Notifier notifier = mock(Notifier.class);
when(repository.findById(1L))
.thenReturn(Optional.of(new Post(1L, "Hello, Mockito", "anna", false)));
PostService service = new PostService(repository, notifier);
Post post = service.find(1L);
assertEquals("Hello, Mockito", post.title());
}
}mock(PostRepository.class) creates a stand-in. when(repository.findById(1L)).thenReturn(...) reads as "when findById(1L) is called, return this post". The service does not know it talks to a mock: it just calls the interface.
What an unstubbed mock returns
Methods you did not stub return "empty" values:
// src/test/java/com/example/blog/PostServiceMockTest.java
System.out.println(repository.findById(7L));
System.out.println(repository.findAll());
System.out.println(repository.save(new Post(null, "X", "anna", false)));Optional.empty
[]
nullOptional → Optional.empty, collections → empty, numbers → 0, boolean → false, other objects → null. So service.find(7L) on this mock throws PostNotFoundException, which is also worth testing.
Common mistake
when() only works with a call on a mock. If you pass a real object by mistake:
PostRepository repository = new InMemoryPostRepository();
when(repository.findById(1L)).thenReturn(...);org.mockito.exceptions.misusing.MissingMethodInvocationException:
when() requires an argument which has to be 'a method call on a mock'.
For example:
when(mock.getArticles()).thenReturn(articles);Create the object with mock(...). The same error occurs if the method is private or static, or if it is equals() or hashCode() — Mockito cannot stub those through when.