Contents

Mockito Create Spy

Written by: David Vlijmincx

Introduction

With mockito, you can not only create mocks but also spies. A spy behaves the same as a real/normal instance of a class, but mockito tracks all the interactions that happen with the spy.

Create a spy with Mockito

You create a spy by calling the mockito.spy() method with the instance you want to spy on as a parameter. In the following example, I create a spy of the dog instance on line 6. Mockito can use the spy to track the interactions that happen with the instance.

In the example, the bark() method is called and I can use the mockito.verify() to check if this interaction has happened.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class Spying {

    @Test
    void createSpy(){
        Dog myDog = new Dog();
        Dog spyDog = Mockito.spy(myDog);

        spyDog.bark();

        Mockito.verify(spyDog).bark();
    }
}

class Dog {

    public String bark(){
        return "Barking";
    }
}

Create Spy for a final class

When you try to spy on a final class you get the following exception when you use the wrong mock maker:

1
2
3
4
org.mockito.exceptions.base.MockitoException: 
Cannot mock/spy class com.davidvlijmincx.Dog
Mockito cannot mock/spy because :
 - final class

This exception can be fixed by chancing the default mock maker. With Mockito 5 this is no longer needed as the default can create spies for final classes.

If you use mockito 4 you need to include the following dependency to use the mock maker inline. The mock maker inline is the new default mock maker starting with Mockito 5 and is able to mock and spy on final classes.

1
2
3
4
5
6
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>4.2.0</version>
    <scope>test</scope>
</dependency>

This dependency changes the default mock maker to the mock maker inline.

With the mocker maker inline enabled you can now spy on final classes like is done in the following example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
public class Spying {

    @Test
    void createSpy(){
        TheFinalDog myDog = new TheFinalDog();
        TheFinalDog spyDog = Mockito.spy(myDog);

        spyDog.bark();

        Mockito.verify(spyDog).bark();
    }
}

final class TheFinalDog {

    public String bark(){
        return "Barking";
    }
}

In the previous example a spy is created for the final class TheFinalDog.

Create a spy using @spy annotation

We can make the test more readable by using Mockito's annotation @Spy. On lines 7 and 8 in the following example, I create an instance and a spy of the Dog class on a single line. Inside the test method, I can directly use the spy.

Read this article if you want to know more about using Mockito's annotations with JUnit.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// use with Mockito 4 / JUnit 4
// @RunWith(MockitoJUnitRunner.class)
// use with mockito 5 / Junit 5
@ExtendWith(MockitoExtension.class)
public class Spying {

    @Spy
    Dog spyDog = new Dog();

    @Test
    void createSpy(){
        spyDog.bark();
        
        Mockito.verify(spyDog).bark();
    }
}

class Dog {

    public String bark(){
        return "Barking";
    }
}

Alter behavior of a spy

You can also stub methods calls just as you do with mocks. In the following example, I use the spy of the dog instance to stub a method. On line 9, I stub the bark() method and make it return a different value than the original.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@ExtendWith(MockitoExtension.class)
public class Spying {

    @Spy
    Dog spyDog = new Dog();

    @Test
    void createSpy(){
        Mockito.when(spyDog.bark()).thenReturn("i am spying");
        String bark = spyDog.bark();

        Mockito.verify(spyDog).bark();
        Assertions.assertEquals("i am spying", bark);
    }
}

class Dog {

    public String bark(){
        return "Barking";
    }
}

Difference with mocks

Mockito tracks the behavior of your objects when you either create a mock of a spy. The biggest difference lies in its default behavior. When you create a mock every call is stubbed and returns null by default and not calling any real object. When you create a spy every method by default calls the real instance.

Depending on the default behavior you need to choose whichever works best for your use case.

Conclusion

This post shows how to create a spy using an existing instance by calling the Mockito.spy method. to make the test more readable you can also use the annotation @spy to create a spy. The last example shows that you can stub method calls to a spy instance.

Further reading

More about testing in Java: