Mockito basic use and more

To do TDD it is very important to have a framework like mockito, JUnit, to test behaviors and results without the need to test the code in broad strokes.

TDD is writing the tests first before writing a line of production code, writing the tests that test the production code, although it could be difficult to do if you don’t have code to compile, so you should write the minimum for a happy compilation. A team does TDD and at the same time it gets the tests done, it also implements the functionality at the same time.

The agile manifesto says value working code over comprehensive documentation, with TDD you have to work with code and test that the code works at the same time, if we use the JavaDocs and TDD, we could get a nice and good documentation in parallel with the delivery of the test functionalities.

A simple code

CupCake will be our class to test in which we will use the annotation @InjectMocks

/**
 * Simple CupCake class for @InjectMocks
 */
public class CupCake {

    public static final String INGREDIENT_FOR_CUPCAKE = "Ingredient is: ";

    private final Ingredient ingredient;
    private List<String> ingredients;
    private final SpyMePlease spyMePlease;

    public CupCake(final Ingredient ingredient, final List<String> ingredients, final SpyMePlease spyMePlease) {
        this.ingredient = ingredient;
        this.ingredients = ingredients;
        this.spyMePlease = spyMePlease;
    }

    public String getIngredient() {
        return INGREDIENT_FOR_CUPCAKE.concat(ingredient.getFlavor());
    }

    public int getIngredients() {
        return ingredients.size();
    }

    /**
    *
    * Aquí podemos obtener un resultado diferente al usar @Mock o @Spy
    *
    * @return boolean
    */
    public boolean canISpyThere() {
        System.out.println("Can I Spy there ?");
        spyMePlease.hola();
        return true;
    }

}

Our Mock

The Ingredient class will be our Mock since this is a dependency of CupCake, all our dependencies will be annotated with @Mock, we could also use @Spy to be injected inside the bean annotated with @InjectMocks.

/**
 * Simple class Ingredient to @Mock
 */
public class Ingredient {

    private String flavor;

    public Ingredient(final String flavor) {
        this.flavor = flavor;
    }

    public String getFlavor() {
        return flavor;
    }
}
/**
 * Clase a usar con @Spy para inspeccionar funcionalidad
 * interna y externa
 */
public class SpyMePlease {

    public void hola() {
        System.out.println("Your are Spying this!");
    }

}

With the @Spy annotation we can test internal and external behaviors of the methods, however with @Mock only the external behaviors.

/**
 * En este caso las dependencias como Ingredient e Ingredients serán nuestros mocks anotadas con
 * @Mock donde esta creara las implementaciones que necesitemos.
 *
 * Mientras que nuestra clase CupCake sera la que vamos a testear, anotada con @InjectMock sin necesidad
 * de instanciarla, luego esta inyectara todos los mocks que están marcados con @Mock, @Spy etc
 */
@ExtendWith(MockitoExtension.class)
class MockingAndSpyTest {

    private static final String BLACK_BERRY = "BlackBerry";

    @Mock
    private Ingredient ingredient;

    @Mock
    private SpyMePlease spyMePlease;

    @Spy
    private List<String> ingredients = new ArrayList<>();

    @InjectMocks
    private CupCake cupCake;

    @Test
    void mockAndSpy() {
        Mockito.when(ingredient.getFlavor()).thenReturn(BLACK_BERRY);

        ingredients.add("Banana");
        ingredients.add("Orange");
        ingredients.add("BlueBerry");

        final int actualIngredients = 3;
        assertThat(actualIngredients, is(cupCake.getIngredients()));

        final String actualFlavor = CupCake.INGREDIENT_FOR_CUPCAKE.concat(BLACK_BERRY);
        assertThat(actualFlavor, is(cupCake.getIngredient()));

        assertThat(true, is(cupCake.canISpyThere()));
    }
}

If we run the test the result would be GREEN and also the following:

Can I Spy there ?


Process finished with exit code 0

But what if we change

@Spy
private SpyMePlease spyMePlease;
Can I Spy there ?
Your are Spying this!


Process finished with exit code 0

We have this warning thanks to the @Spy annotation and openjdk-11 version

WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.mockito.internal.util.reflection.ReflectionMemberAccessor (file:/C:/Users/Rubn/.m2/repository/org/mockito/mockito-core/3.6.0/mockito-core-3.6.0.jar) to field java.util.ArrayList.elementData
WARNING: Please consider reporting this to the maintainers of org.mockito.internal.util.reflection.ReflectionMemberAccessor
WARNING: Use --illegal-access=warn to enable warnings of further illegal reflective access operations
WARNING: All illegal access operations will be denied in a future release

It is solved with 2 dependencies:

  • mockito-core

  • mockito-inline

<!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
<dependency>
   <groupId>org.mockito</groupId>
   <artifactId>mockito-core</artifactId>
   <version>3.6.0</version>
   <scope>test</scope>
</dependency>

<!-- https://mvnrepository.com/artifact/org.mockito/mockito-inline -->
<dependency>
   <groupId>org.mockito</groupId>
   <artifactId>mockito-inline</artifactId>
   <version>3.6.0</version>
   <scope>test</scope>
</dependency>
These above dependencies are not needed if we use Springboot

Refactoring the test a little

... imports removed for brevity

@DisplayName("We use @InyectMocks and pass the dependencies via constructor")
@ExtendWith(MockitoExtension.class) (1)
class CupCakeTest {

    @InjectMocks
    private CupCake cupCake; (2)

    @Mock
    private Ingredient ingredient; (3)

    @Mock
    private List<Ingredient> ingredientList;

    @Mock
    private SpyMePlease spyMePlease;

    @Test
    @DisplayName("The tomato ingredient is tested here and other different ingredients are tested here")
    void getIngredient() {

        when(ingredient.getFlavor()).thenReturn("tomate"); (4)
        when(ingredientList.get(0)).thenReturn(new Ingredient("Banana"));
        when(ingredientList.get(1)).thenReturn(new Ingredient("Fresa"));
        when(ingredientList.get(2)).thenReturn(new Ingredient("Coco"));

        assertThat("Ingredient is: tomate").isEqualTo(cupCake.getIngredient());
        assertThat("Banana").isEqualTo(cupCake.getIngredients().get(0).getFlavor());
        assertThat("Fresa").isEqualTo(cupCake.getIngredients().get(1).getFlavor());
        assertThat("Coco").isEqualTo(cupCake.getIngredients().get(2).getFlavor());

        verify(ingredient).getFlavor();

    }

    @Test
    @DisplayName("The ingredient list shall have a size of 3 ingredients")
    void getSizeOfAllIngredients() {
        when(ingredientList.size()).thenReturn(3);
        assertThat(3).isEqualTo(cupCake.getSizeOfAllIngredients());
    }

    @Test
    @DisplayName("The list should be the same as our cupCake, and contain a Banana flavored ingredient.")
    void getIngredients() {

        when(this.ingredientList.size()).thenReturn(1);
        when(this.ingredientList.get(0)).thenReturn(new Ingredient("Banana"));

        assertAll( (5)
                () -> assertEquals(this.ingredientList.size(), cupCake.getIngredients().size()),
                () -> assertEquals(this.ingredientList.get(0), this.cupCake.getIngredients().get(0))
        );

        verify(this.ingredientList, times(2)).size(); (6)
        verify(this.ingredientList, times(2)).get(0);

    }

    @Test
    @DisplayName("The method will return true stupidly")
    void canISpyThere() {

        assertThat(cupCake.canISpyThere()).isTrue();
    }

}
1 Annotation required for JUnit 5
2 Our class to test CupCake
3 Everything annotated with @Mock we must simulate it, that is, give it behavior
4 We make sure to invoke the when in this method to avoid an UnnecessaryStubbingException type error.
5 assertAll of JUnit 5
6 We verify that size and get are invoked only 2 times.
TIPS @BeforeEach y @InyectMocks invocar a contructor o no
  1. If we create a setup type method with @BeforeEach we must be aware to avoid UnnecessaryStubbingException and invoke the when in the corresponding methods not in all, as well as knowing if it is necessary or not to create a contructor of the class to test since Mockito knows when to use the corresponding contructor, the documentation of @InyectMocks discusses in more depth the use cases.

  2. In this case I did not use an setup with @BeforeEach, but I should have known when to invoke the Mockito.when to give behavior in each method.

  3. If our constructor of the class to be tested has a dependency that cannot be simulated, we should instantiate that constructor by hand and pass the instance variables by hand, since Mockito will not do it, that is to say, we have to know the minimum operation of the @InyectMocks annotation.

Simple testing of empty or void method

class SpyMePleaseTest {

    private SpyMePlease spyMePlease = new SpyMePlease();

    @Test
    @DisplayName("Testing hello method, and it does not throw any exception.")
    void hola() {
        assertThatCode(() -> spyMePlease.hola()).doesNotThrowAnyException();  (1)
    }
}
1 We can help ourselves with the AssertionsForClassTypes class to test empty methods, which sometimes are a bit annoying.

Verify and Mockito times

Let’s see a simple service that displays only 10 numbers in a reactive way and in case one of them fails our stream triggers a fallback.

With reactive programming the testing part is usually very rough, but with StepVerifier a dependency that is useful to do TDD of reactive streams since the create method accepts a publisher as such, either from project reactor or even RxJava or some other implementation of the reactive stream specification.

It can happen with project reactor that our reactive stream triggers a fallback many times, which should be avoided.
public class MyReactorServiceImpl implements MyReactorService {

    private FallBackService service; (1)

    public Flux<Integer> createTenItem() {
        return Flux.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)//Flux.range :D
                .doOnError(error -> log.info("Error with item {}", error))
                .flatMap(item -> this.launchError(item)
                        .onErrorReturn(-1)) (2)
                .doOnNext(onNext -> log.info("onNext {}", onNext));
    }

    public Mono<Integer> launchError(final Integer item) {
        if (item == 6) {
            return Mono.error(new RuntimeException("Error con item " + item));
        }
        return Mono.just(item);
    }

}
1 Without using our fallback service yet.
2 When our item is 6 we will return with onErrorReturn( a fallback of help, but not so smart) a -1,
@Test
@DisplayName("When it is 6, an error is produced and we return -1")
void createTenItem() {
    StepVerifier.create(myReactorService.createTenItem())
            .expectNext(1, 2, 3, 4, 5, -1, 7, 8, 9 , 10) (1)
            .verifyComplete();
}
1 We are working with a Flux, that is, item`s…​. and when it is 6 it is actually an error that we will convert to -1.

excepcion 1 reactor

The -1 we return in case of error with the number 6, but the Stream operation continues as normal.

More powerful fallback

public class MyReactorServiceImpl implements MyReactorService {

    private final MyFallbackServiceImpl service;

    public Flux<Integer> createTenItem() {
        return Flux.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)// Flux.range :D
                .doOnError(error -> log.info("Error with item {}", error))
                .flatMap(item -> this.launchError(item) (1)
                        .switchIfEmpty(this.fallback())) (3)
                .doOnNext(onNext -> log.info("onNext {}", onNext));
    }

    public Mono<Integer> launchError(final Integer item) {
        if (item == 6) {
            return Mono.empty(); (2)
        }
        return Mono.just(item);
    }

    public Mono<Integer> fallback() {
        log.info("fallback {}", -1);
        return Mono.just(-1); (4)
    }

}
1 We fire in case of error when it is 6
2 When it is 6 we send a Mono.empty to use it with the switchIfEmpty instead of a RuntimeException
3 We go for the SwitchIfEmpty
4 We are already in the fallback and return -1
Our fallback is printed many times, and besides that it is badly tested, that is to say, it lacks a Mockito.verify and Mockito.times, the ideal would be.

conFallbackEnServicio

We refactored a little MyReactorServiceImpl to use our fallback service to mock it up.

public Flux<Integer> createTenItem() {
    return Flux.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)// Flux.range :D
            .doOnError(error -> log.info("Error with item {}", error))
            .flatMap(item -> this.launchError(item)
                    .switchIfEmpty(this.service.fallback())) (1)
            .doOnNext(onNext -> log.info("onNext {}", onNext));
}

public Mono<Integer> launchError(final Integer item) {
    if (item == 6) {
        return Mono.empty();
    }
    return Mono.just(item);
}
1 We use our fallback service, i.e. our dependency, to check its behavior in a more intelligent way.

We refactored our test a bit

@Test
@DisplayName("When it is 6, an error occurs and we return  -1.")
void createTenItem() {

    when(service.fallback()).thenReturn(Mono.just(-1)); (1)

    StepVerifier.create(myReactorService.createTenItem())
            .expectNext(1, 2, 3, 4, 5, -1, 7, 8, 9, 10)
            .verifyComplete();

    Mockito.verify(service, Mockito.times(1)).fallback(); (2)
}
1 A small mock of our fallback service
2 We verify that our fallback is executed only once, which would be ideal, right?

Surprise, switchIfEmpty dangerous

Our fallback has been invoked 10 damn times which is what we don’t want, ufff the DevOps will hate me if an incidence comes out at 12am because I blow up mongo lmao

diezInvocacionesMuyMal

Using the defer operator as our ally

To solve it we use the operator defer, which will practically make the fallback to fire when we have an empty just with the number 6 (our error) a little crazy isn’t it? and that the SwitchIfEmpty works correctly.

public Flux<Integer> createTenItem() {
    return Flux.just(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)// Flux.range :D
            .doOnError(error -> log.info("Error with item {}", error))
            .flatMap(item -> this.launchError(item)
                    .switchIfEmpty(Mono.defer(this.service::fallback)
                            .doOnNext(onNext -> log.info("Fallback {}", -1)))) (1)
            .doOnNext(onNext -> log.info("onNext {}", onNext));
}
1 Added the defer operator and a doOnNext that will print Fallback -1 once.

Our test will pass quietly now

test green con defer

Using ArgumentsSource for Parameterized tests

With JUnit5 we can use a parameterized list with data, much more data, that we can even read from some file, etc, in this case we create them ourselves, for example

public class CupCakeArgumentsProvider implements ArgumentsProvider { (1)

    @Override
    public Stream<? extends Arguments> provideArguments(ExtensionContext extensionContext) throws Exception {

        final Ingredient pera = new Ingredient("pera");
        final Ingredient manzana = new Ingredient("manzana");
        final Ingredient coco = new Ingredient("coco");
        final List<Ingredient> ingredientList = Arrays.asList(pera, manzana, coco);

        final Ingredient pina = new Ingredient("piña");
        final Ingredient frutaDelDragon = new Ingredient("Fruta del dragon");
        final Ingredient uva = new Ingredient("uva");
        final List<Ingredient> ingredientList1 = Arrays.asList(pina, frutaDelDragon, uva);

        return Stream.of(
                Arguments.of(ingredientList, pera, manzana, coco),
                Arguments.of(ingredientList1, pina, frutaDelDragon, uva)); (2)
    }
}
1 We extend the ArgumentsProvider interface
2 We return a Stream with 2 Arguments.of parameters with this order that we must respect in the signature of our test
@ParameterizedTest (1)
@ArgumentsSource(CupCakeArgumentsProvider.class) (2)
@DisplayName("Using many more ingredients")
void masFrutas(final List<Ingredient> ingredientsList, Ingredient ingredientExpected, Ingredient ingredient2Expected,
                   Ingredient ingredient3Expected) { (3)

        when(this.ingredientList.get(0)).thenReturn(ingredientsList.get(0));
        when(this.ingredientList.get(1)).thenReturn(ingredientsList.get(1));
        when(this.ingredientList.get(2)).thenReturn(ingredientsList.get(2));

        assertNotNull(ingredientsList);
        assertThat(ingredientExpected).isEqualTo(cupCake.getIngredients().get(0)); (4)
        assertThat(ingredient2Expected).isEqualTo(cupCake.getIngredients().get(1));
        assertThat(ingredient3Expected).isEqualTo(cupCake.getIngredients().get(2));

        assertThat(ingredientList)
                .usingRecursiveComparison()
                .isEqualTo(cupCake.getIngredients());

        verify(ingredientList, times(1)).get(0);
}
1 Necessary notation
2 ArgumentsSource goes at the method level and is used with the class that implements the ArgumentsProvider
3 This method signature must match the parameters set in the Arguments.of i.e. a list followed by 3 Ingredients for this to match, also that this method will be invoked twice, because we use two Arguments.of
4 We pass an expected and parameterized Ingredient that must be the same that should have the list of ingredients inside our cupCake.
If our method signature does not match, we will get an error type ParameterResolutionException

Integration test with BlockHound

Here we load the spring context, with the annotation @ContextConfiguration and apply stereotyping to the service we are going to need, that is to say a real service as is, which can even invoke a database if desired, ideally the test name should also end in IT(integration).

According to the documentation, BlockHound is scanning the Scheduler parallel by default and if we in our code or the internal code of any dependency at that time has a blocking call, BlockHound will complain and throw exception. You can change this behavior by instantiating your Builder as indicated in the documentation.

@DisplayName("<= Using blockhound to detect blocking calls in parallel thread =>")
@Log4j2
@ContextConfiguration(classes = {ReactiveRandomNumbers.class}) (1)
@ExtendWith(SpringExtension.class) (2)
class UsingBlockHoundIT {

    @Autowired (3)
    private ReactiveRandomNumbers reactiveRandomNumbers;

    @BeforeEach
    void setup() {
        BlockHound.install(); (4)
    }

    @Test
    @DisplayName("Blocking call! in line 37")
    void detectBlockingCall1() {
        StepVerifier.create(Mono.just(1)
                .doOnNext(e -> this.blockMe()) (5)
                .subscribeOn(Schedulers.parallel()))
                .expectErrorMatches(error -> error.getMessage().contains("Blocking call!"))
                .verify();
    }

    @Test
    @DisplayName("Blocking call! in line 46")
    void detectBlockingCall2() {
        StepVerifier.create(this.reactiveRandomNumbers.monoWithBlockingCallInside(500L)
                        .subscribeOn(Schedulers.parallel())) (6)
                .expectErrorMatches(error -> error.getMessage().contains("Blocking call!"))
                .verify();
    }

    @Test
    @DisplayName("Blocking call! in line 57, but using boudendElastic to avoid that")
    void avoidBlockingCall() {

        StepVerifier.create(Mono.just(1)
                        .doOnNext(e -> this.blockMe())
                        .subscribeOn(Schedulers.boundedElastic())) (7)
                .expectNext(1)
                .verifyComplete();
    }

    @RepeatedTest(10)
    @DisplayName("Blocking call! in line 67, but using boudendElastic to avoid that")
    void avoidBlockingCall2() {

        StepVerifier.create(this.reactiveRandomNumbers.monoWithBlockingCallInside(500L)
                        .subscribeOn(Schedulers.boundedElastic())) (8)
                .expectNextMatches(map -> map.size() == 6)
                .verifyComplete();
    }

    @SneakyThrows
    void blockMe() { (9)
        Thread.sleep(1000);
    }
}
1 We load our ReactiveRandomNumbers service.
2 We enable JUnit 5 with Spring.
3 We inject our service as is, to make a real invocation, without mockeying.
4 The magic, to install our BlockHound agent, and with @BeforeEach to trigger on every test.
5 We insert a blocking call to check that our agent detects it, and it does detect it.
6 We use the Scheduler parallel, to scan it and blockHound detects a blocking internal call.
7 Now we use a boundedElastic to avoid that blocking call.
8 The same in the invoked service.
9 The additional test blocking method with a 1 second sleep.

This service ReactiveRandomNumbers internally when creating pseudo random numbers bursts here, but it is already an internal thing that we are not handling, and there is useful blockhound that indicates us that there is contention to take into account.

Caused by: reactor.blockhound.BlockingOperationError: Blocking call! java.io.FileInputStream#readBytes