Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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 MockitoBDDMockito
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.PostServiceBddTest

Which 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.

Thanks for reading!

Donate to the site — we work hard for you.