BDD style: given, willReturn, then, should
BDD (behavior-driven development) describes behavior in three parts: given a situation, when something happens, then we expect a result. It is the same arrange, act, assert, only in other words. The catch with plain Mockito is that the "given" part starts with the method when(...), which confuses the reading. The class BDDMockito gives the same features with names that fit: given(...).willReturn(...) instead of when(...).thenReturn(...) and then(mock).should() instead of verify(mock). BDDMockito is part of Mockito — nothing to add to pom.xml.
given and then
Create src/test/java/com/example/blog/PostServiceBddTest.java:
// src/test/java/com/example/blog/PostServiceBddTest.java
package com.example.blog;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import java.util.Optional;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.*;
@ExtendWith(MockitoExtension.class)
class PostServiceBddTest {
@Mock
PostRepository repository;
@Mock
Notifier notifier;
@InjectMocks
PostService service;
@Test
void givenDraft_whenPublish_thenAuthorIsNotified() {
// given
given(repository.findById(1L))
.willReturn(Optional.of(new Post(1L, "Hello", "anna", false)));
// when
Post post = service.publish(1L);
// then
assertThat(post.published()).isTrue();
then(notifier).should().send("anna", "Your post \"Hello\" is published");
}
}The three comments split the test into parts, and each part uses its own word. then(notifier).should() is the same as verify(notifier).
The whole dictionary
| Plain Mockito | BDDMockito |
|---|---|
when(mock.find()).thenReturn(x) | given(mock.find()).willReturn(x) |
when(...).thenThrow(e) | given(...).willThrow(e) |
doThrow(e).when(mock).send(...) | willThrow(e).given(mock).send(...) |
verify(mock).save(...) | then(mock).should().save(...) |
verify(mock, never()).save(...) | then(mock).should(never()).save(...) |
verifyNoInteractions(mock) | then(mock).shouldHaveNoInteractions() |
// src/test/java/com/example/blog/PostServiceBddTest.java
@Test
void givenNoPost_whenPublish_thenNothingIsSaved() {
given(repository.findById(99L)).willReturn(Optional.empty());
assertThatThrownBy(() -> service.publish(99L))
.isInstanceOf(PostNotFoundException.class);
then(repository).should(never()).save(any(Post.class));
then(notifier).shouldHaveNoInteractions();
}
@Test
void givenMailIsDown_whenPublish_thenErrorIsPassedOn() {
given(repository.findById(1L))
.willReturn(Optional.of(new Post(1L, "Hello", "anna", false)));
willThrow(new IllegalStateException("Mail server is down"))
.given(notifier).send(any(), any());
assertThatThrownBy(() -> service.publish(1L))
.hasMessage("Mail server is down");
}[INFO] Running com.example.blog.PostServiceBddTest
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.788 s -- in com.example.blog.PostServiceBddTestWhich style to choose is a matter of taste. Pick one per project and do not mix when and given in one class.
Naming: given, when, then
The names follow the same idea: givenDraft_whenPublish_thenAuthorIsNotified. The underscores separate the parts, and a failure in the report already tells the story. Another popular form is publish_sendsNotification_whenDraftExists, or @DisplayName("a draft, once published, notifies its author"). Whatever the form, the name should contain the situation and the expected result, not just the method name.
The @Spy annotation
In the spies lesson spies were created with spy(...). With MockitoExtension the annotation @Spy does the same, and @InjectMocks passes the spy to the service like any mock:
// src/test/java/com/example/blog/SpyAnnotationTest.java
@ExtendWith(MockitoExtension.class)
class SpyAnnotationTest {
@Spy
InMemoryPostRepository repository;
@Mock
Notifier notifier;
@InjectMocks
PostService service;
@Test
void givenRealRepository_whenCreate_thenPostIsStored() {
Post post = service.create("Hello", "anna");
assertThat(repository.findAll()).containsExactly(post);
then(repository).should().save(any(Post.class));
}
}Mockito creates new InMemoryPostRepository() itself and wraps it in a spy; the real save() stores the post. You can also write @Spy InMemoryPostRepository repository = new InMemoryPostRepository(); when the constructor needs arguments. @Spy needs a real class: on an interface such as PostRepository there is no code to call, and every method returns an empty value.
Common mistake
AssertJ has its own BDD class BDDAssertions with a method then(value). With both imports in one file, the compiler cannot tell the two then apart:
import static org.assertj.core.api.BDDAssertions.*;
import static org.mockito.BDDMockito.*;[ERROR] /.../src/test/java/com/example/blog/ThenClashTest.java:[34,9] reference to then is ambiguous
both method <T>then(T) in org.assertj.core.api.BDDAssertions and method <T>then(T) in org.mockito.BDDMockito match
[ERROR] /.../src/test/java/com/example/blog/ThenClashTest.java:[34,23] cannot find symbol
symbol: method should()
location: class org.assertj.core.api.ObjectAssert<com.example.blog.Notifier>Keep org.mockito.BDDMockito.* for mocks and use assertThat from org.assertj.core.api.Assertions for values, as in this lesson.