Pages

Monday, May 11, 2020

Mockito – Using Spies Examples: Mock Vs Spy

1. Introduction


In this tutorial, You'll be learning how to use Mockito Spy() method and @Spy annotation. How to mock a stub with spy and At last what is the difference between mock() and spy().

Example of Mock Vs Spy methods of Mockito. Many of the developers don't know when to use which one.

Add maven mockito dependency in the pom.xml file.

Please read the full article then at the end once you see the Mock Vs Spy, you will get a clear understand of both methods. Both are very powerful methods.

<dependency>
  <groupId>org.mockito</groupId>
  <artifactId>mockito-core</artifactId>
  <version>3.3.3</version>
  <scope>test</scope>
</dependency>


Mockito – Using Spies Examples Mock Vs Spy

Mockito – Using Spies

2.  Mockito.Spy() Method Example


Now, You'll see how to use the spy() method. This is a static method that can be invoked by its class name such as Mockito.spy().

This is mainly used to spy the real object rather than working with dummy objects.

To create ArrayList spy object, just use Mockito.spy(ArrayList.class). Actually, This creates a real ArrayList object.

When use spy() method on any class then you can call the real methods of that class.

In the below example, you have spied ArrayList class so you can invoke actual ArrayList add(), remove() methods.

We have added three string objects to the List and printing the list to see its values.



package com.javaprogramto.mockito;


import org.junit.Assert;
import org.junit.Test;
import org.mockito.Mockito;

import java.util.ArrayList;
import java.util.List;

public class MockitoSpyTest {

    @Test
    public void spyArrayList() {


        List<String> spyList = Mockito.spy(ArrayList.class);

        spyList.add("Hello");
        spyList.add("mockito");
        spyList.add("spy");

        System.out.println("Spy list : "+spyList);
        Assert.assertEquals(3, spyList.size());
    }
}


Output:

[Spy list : [Hello, mockito, spy]]

Observe the output which prints the actual values that are added to the list. In the above example, the real add() method called three times to add values.

And also you can verify whether the add method is invoked with a specific value as below.

@Test
public void spyArrayListVerify() {

    List<String> spyList = Mockito.spy(ArrayList.class);

    spyList.add("Hello");
    spyList.add("mockito");
    spyList.add("spy");

    Mockito.verify(spyList).add("spy");
    Mockito.verify(spyList).add("Hello");
    Mockito.verify(spyList).add("mockito");

    Assert.assertEquals(3, spyList.size());
}

3. @Spy Annotation Examples


Next, you'll see now how to use @Spy annotation on the spy objects. This is an alternative to the spy() method.

@SpyList<String> spyAnnotation = new ArrayList<>();

@Testpublic void spyArrayListAnnotation() {

    spyAnnotation.add("Hello");
    spyAnnotation.add("spy");

    Mockito.verify(spyAnnotation).add("spy");
    Mockito.verify(spyAnnotation).add("Hello");

    Assert.assertEquals(3, spyAnnotation.size());
}

If you run this test method without enabling the needed configurations then it will give an error.

You must annotate the test class with @RunWith() annotation and pass JunitMockitoRunner.class to the annotation before using @mock or @Spy annotations.

or

@RunWith(MockitoJUnitRunner.class)

public class MockitoSpyTest {


4. Stubbing a Spy


Now, You'll learn how to stub a spy object with a spy() method. By using the stub() method, you can override the actual behavior of the original methods.

Let us write a example stub() method to return size 50 when ArrayList.size() method is invoked.

@Testpublic void stubSpyArrayList() {

    List<String> stubList = Mockito.spy(ArrayList.class);

    Assert.assertEquals(0, stubList.size());

    Mockito.doReturn(50).when(stubList).size();

    Assert.assertEquals(50, stubList.size());

    Mockito.doReturn(false).when(stubList).isEmpty();

    Assert.assertEquals(false, stubList.isEmpty());
}


This code works and returns 50 when the stubList.size() method is called.

And also, this list is empty but we have overridden the isEmpty() value to false even though the list is not having the values.

5. Mock VS Spy in Mockito


Looks mock() and spy() method looks the same but really both are not the same and work in different styles.

As of now, you have seen the spy() method which works exactly the same as the original ArrayList instance and all methods will be executed as regular.

But, When you mock ArrayList with the mock() method will work differently. Whatever you call the methods on the ArrayList to add() method will be the actual calls.

A simple ArrayList mocking with mock() methods.

@Testpublic void mockArrayList() {

    List<String> mockList = Mockito.mock(ArrayList.class);

    mockList.add("mock");

    Assert.assertEquals(1, mockList.size());
}

Output:

This code produces the error message saying actual mockList size is 0 but we added a value "mock" to it.


java.lang.AssertionError:
Expected :1
Actual   :0
<Click to see difference>


at org.junit.Assert.fail(Assert.java:88)
at org.junit.Assert.failNotEquals(Assert.java:743)
at org.junit.Assert.assertEquals(Assert.java:118)
at org.junit.Assert.assertEquals(Assert.java:555)
at org.junit.Assert.assertEquals(Assert.java:542)
at com.javaprogramto.mockito.MockitoSpyTest.mockArrayList(MockitoSpyTest.java:89)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.mockito.internal.runners.DefaultInternalRunner$1$1.evaluate(DefaultInternalRunner.java:46)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:77)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:83)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)


Actually added value to the List but those are reflected in the list. The mock () method does not run any actual methods.

Let us change the mock() to spy and see the output.

spy() method will wrap the existing instance into a spy object and it will behave the same as the original list object. add() method will be called from original ArrayList and value will be added to the underlying ArrayList.

@Testpublic void spysArrayList() {

    List<String> mockList = Mockito.spy(ArrayList.class);

    mockList.add("mock");

    Assert.assertEquals(1, mockList.size());
}

This code works perfectly fine without any errors.

As a result of Mock Vs Spy is You should prefer using the mock() method instead of spy() method because if you don't override the behaviors then it executes the original core logic methods.

6. understanding Most Common Mockito NotAMockException


The most commonly seen error is "Mockito NotAMockitoException" when any method is called on a non mocked object as below.


    Mockito.doReturn(50).when(stubList).size();


If the stubList is not a mocked or spy object then it will throw a runtime exception.

org.mockito.exceptions.misusing.NotAMockException:
The argument passed to when() is not a mock!
Example of correct stubbing:
    doThrow(new RuntimeException()).when(mock).someMethod();

at com.javaprogramto.mockito.MockitoSpyTest.spysArrayList(MockitoSpyTest.java:97)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.mockito.internal.runners.DefaultInternalRunner$1$1.evaluate(DefaultInternalRunner.java:46)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
at org.mockito.internal.runners.DefaultInternalRunner$1.run(DefaultInternalRunner.java:77)
at org.mockito.internal.runners.DefaultInternalRunner.run(DefaultInternalRunner.java:83)
at org.mockito.internal.runners.StrictRunner.run(StrictRunner.java:39)
at org.mockito.junit.MockitoJUnitRunner.run(MockitoJUnitRunner.java:163)
at org.junit.runner.JUnitCore.run(JUnitCore.java:160)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)


7. Conclusion


In this article, You've seen how to mock an object with a spy() and mock() methods.

What are the common errors you get when you working with the Mockito framework?

And also we've seen what is the difference between mock() vs Spy() methods with examples.


Ref

All the code is shown in this article is over GitHub.

You can download the project directly and can run in your local without any errors.



If you have any queries please post in the comment section.

No comments:

Post a Comment

Please do not add any spam links in the comments section.