How to mock a method which belongs to the same class you're testing?

How to mock a method which belongs to the same class you're testing?

In this post, learn how to mock a method using Mockito which belongs to the same class you're testing in Spring Boot.

The core principle of unit testing a piece of code is to isolate it from all other components and test its core functionality. This is easily enabled by mocking dependencies and other components.

Given below is a common problem I've encountered before while writing unit tests for my code.

Consider the following MyFunClass.java which contains two methods: getFlag() and getFlagNeg().

public class MyFunClass {
    public Boolean getFlag() {
        return !getFlagNeg();
    }
    public Boolean getFlagNeg(){
        return false;
    }
}

Obviously, the code is pretty much useless, but it resembles a common pattern we see in clean code, where a function is expected to do only one thing.

Consider that we now want to unit-test getFlag() method.

Here one can easily run into a problem. If we call getFlag() on a MyFunClass object in a unit test, it will call getFlagNeg() as well, which violates the principle of unit-testing, since now we're testing two methods with one unit test, instead of unit-testing both methods individually.

How to solve this? Well, what we want here is this:

Call to getFlag()REAL
Call to getFlagNeg()FAKE

We can do this in two ways:

1. Using @Mock

Mocking the object you're testing? Huh. Then what's the point of testing since all methods called on mocked object will return a default value (null)?

Hence, we have to map the getFlag() method to the REAL getFlag() of MyFunClass method. We can do this via Mockito.when().thenCallRealMethod() function provided by Mockito.

We also have to configure getFlagNeg() to return a boolean value, which will be called during the test by the object under the test. Since we're calling this method on a mocked object, it will not call the real method.

@ExtendWith(MockitoExtension.class)
public class FunClassTest {
    @Mock
    MyFunClass underTest = new MyFunClass();

    @Test
    public void test_getFlag(){
        Mockito.when(underTest.getFlag()).thenCallRealMethod();
        Mockito.when(underTest.getFlagNeg()).thenReturn(false);
        Assertions.assertEquals(true, underTest.getFlag());
    }

}

2. Using @Spy

Since spying an object retains original behaviour of that object, all method calls on a spy call the real method whilst tracking these behaviour.

Hence, we can also create a spy of MyFunClass.

Then we have to make sure that getFlagNeg() doesn't call the real method, so we can configure it to return a boolean value using then-when statements.

Note that calling getFlag() on a spy will call the real method by default.

@ExtendWith(MockitoExtension.class)
public class FunClassTest {
    @Spy
    MyFunClass underTestSpy = new MyFunClass();

    @Test
    public void test_getFlag(){
        Mockito.when(underTestSpy.getFlagNeg()).thenReturn(false);
        Assertions.assertEquals(true, underTestSpy.getFlag());
    }

}

Thanks for reading, subscribe for more such posts.!