das
das

Reputation: 679

Junit Mockito mock not working as expected

I have a TestClass and I am writing unit test cases for calculate() method of TestClass. Please refer to the code below.

public class TestClass {

    public int calculate() {

        System.out.println("calculating area");
        String shape = getShape(1);
        int radius = 10;
        int result = findArea(shape, radius);
        return result;
    }

    public String getShape(int index) {
        if(index == 1) {
            return "circle";
        }
        else {
            return "square";
        }
    }

    public int findArea(String shape, int radius) {
        if(shape == "circle") {
            return 3 * radius * radius;
        }
        else {
            return radius * radius;
        }
    }
}

While mocking getShape() function, I want the mock to return "square" when I pass the value 1. Please refer to the unit test below.

import static org.junit.jupiter.api.Assertions.assertEquals;

import org.junit.Before;
import org.junit.jupiter.api.Test;
import org.mockito.Mockito;

public class TestClassTest {
    //TestClass testClass;

    @Before
    public void init() {
        TestClass testClass = new TestClass();
        System.out.println("Inside before");
    }

    @Test
    public void calculateTest() {
        int expected = 100;
        TestClass testClass = new TestClass();
        TestClass testClassMock = Mockito.mock(TestClass.class);
        Mockito.when(testClassMock.getShape(1)).thenReturn("square");
        int actual = testClass.calculate();
        assertEquals(expected, actual);
    }

}

The test is failing with error, expected 100, but was 300. So it's clear that the getShape(1) returns value circle, instead of the value I provided using Mockito.when(). Please let me know what mistake I'm making.

Upvotes: 3

Views: 8852

Answers (4)

Abhishek
Abhishek

Reputation: 11

Mocking only when using the object as spring instance is the primary requirement for mocking and that has been discussed often.

its also important to understand the sequence of mockito.doReturn() and mockito.init(). Make sure mockito.init is called first.

you can achieve that through annotation to make sure init() is always first. -AB

Upvotes: 1

pirho
pirho

Reputation: 12235

As also the other answer suggests you can use a spy. I would rather initialize it with @Spy annotation and get totally rid of your @Before method, so like:

@Spy
private TestClass testClass;

@Test
public void calculateTest() {
    int expected = 100;
    Mockito.when(testClass.getShape(1)).thenReturn("square");
    int actual = testClass.calculate();
    assertEquals(expected, actual);
}

// This just to show that if you do not spy any methods it works as "normal" TestClass

@Test
public void calculateTestNotSpied() {
    int expected = 300;
    int actual = testClass.calculate();
    assertEquals(expected, actual);
}

Also - I am not sure if it is a big problem - but you seem to mix Junit4 & Junit5 (Jupiter) annotations and assertions. It might be a good idea to use only one of them (4|5) in your tests.

Upvotes: 2

krishna Prasad
krishna Prasad

Reputation: 3812

Problem is when you have created two separate instances of class TestClass and then calling the method calculate with one instance of class TestClass testClass and you have Mocked and assigned the return values in the separate instance testClassMock, so the mocked values do not play any roles while you are running the test cases.

    TestClass testClass = new TestClass();
    TestClass testClassMock = Mockito.mock(TestClass.class);

The simple solution is that you should have single instances for calling the test method and mock method, for more details, visit the below questions:

mock methods in same class

and for you solution, below is the code:

TestClass testClass = Mockito.spy(new TestClass());
Mockito.doReturn("square").when(testClass).getShape(ArgumentMatchers.anyInt());
int actual = testClass.calculate(); 

Upvotes: 0

Mureinik
Mureinik

Reputation: 311853

You're mocking an object, but then you never use this mock. It looks like you meant to spy (i.e., partially mock) the object under test in such a way that getShape is mocked away, but calculate's real implementation is called:

@Test
public void calculateTest() {
    int expected = 100;
    TestClass testClass = Mockito.spy(new TestClass());
    Mockito.doReturn("square").when(testClass).getShape(1);
    int actual = testClass.calculate();
    assertEquals(expected, actual);
}

Upvotes: 3

Related Questions