Junit Mockito 模拟没有按预期工作

Junit Mockito mock not working as expected

我有一个 TestClass,我正在为 TestClasscalculate() 方法编写单元测试用例。请参考下面的代码。

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;
        }
    }
}

在mock getShape() 函数时,我希望mock 为return "square" 当我传递值1 时,请参考下面的单元测试。

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);
    }

}

测试失败并出现错误,预期为 100,但实际为 300。所以很明显 getShape(1) returns 值圆,而不是我使用 Mockito.when() 提供的值.请让我知道我犯了什么错误。

你正在模拟一个对象,但是你从来没有使用过这个模拟。看起来您打算以 getShape 被模拟掉的方式监视(即部分模拟)被测对象,但 calculate 的实际实现称为:

@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);
}

问题是当您创建了 class TestClass 的两个单独实例,然后使用 class TestClass testClass 的一个实例调用方法 calculate并且您已经在单独的实例 testClassMock 中模拟并分配了 return 值,因此当您是 运行 测试用例时,模拟值不会发挥任何作用。

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

简单的解决方案是你应该有一个单独的实例来调用测试方法和模拟方法,有关更多详细信息,请访问以下问题:

对于您的解决方案,下面是代码:

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

另一个答案也建议您可以使用 spy。我宁愿用 @Spy 注释初始化它并完全摆脱你的 @Before 方法,比如:

@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);
}

此外 - 我不确定这是否是一个大问题 - 但您似乎混合了 Junit4 和 Junit5 (Jupiter) 注释和断言。在测试中只使用其中一个 (4|5) 可能是个好主意。

仅在将对象用作 spring 实例时模拟是模拟的主要要求,并且经常被讨论。

了解 mockito.doReturn() 和 mockito.init() 的顺序也很重要。确保首先调用 mockito.init。

您可以通过注释来确保 init() 始终在第一位。 -AB