【问题标题】:Mocking a method using Mockito使用 Mockito 模拟方法
【发布时间】:2012-05-29 14:25:38
【问题描述】:

使用 mockito 模拟方法是否确保永远不会调用模拟方法?我有 Main 类,其中包含一些我想为其编写单元测试的代码,并且我有一个单元测试类 MainTest,其中包含 Main 类的单元测试。

例如:

源类:

package abc;

public class Main {

    public int check1() {
        int num = 10;
        num = modify(num);
        return num;
    }

    public int modify(int num) {
        if (num % 10 == 0) return num / 10;
        return -1;
    }
}

Junit 测试(使用 mockito)

package abc;

import junit.framework.Assert;

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

public class MainTest {

    private static Main main;

    @BeforeClass
    public static void setUp() {
        main = Mockito.mock(Main.class);
        Mockito.when(main.modify(10)).thenReturn(5);
    }

    @Test
    public void testCheck1() {
        Test1 main1 = new Main();
        int num = main.check1();
        Assert.assertEquals(5, num);
    }
}

此测试失败。为什么?

【问题讨论】:

    标签: unit-testing mockito


    【解决方案1】:

    已编辑 因为您没有为check1() 提供行为。 ALL 方法会被模拟,因此如果您不提供行为,check1() 返回 int 的返回类型的默认值,即 0。此外,check1() 因为它被模拟,所以甚至不会得到拨打modify()

    如果您尝试测试一个类,则永远不要模拟被测类。在极少数情况下,您可能需要监视正在测试的类。相反,您只是在嘲笑合作者。

    我猜你的例子是人为的(我希望如此)。但是,如果您正在编写和测试一个您认为要修改某些内部方法行为的类,我看到两种可能的可能性:

    1. 您可能需要将要模拟的方法的功能重构为协作者类。然后,作为合作者继续模拟这种行为是有意义的。
    2. 您可能还需要修改 API,以便传递将要更改的内容。在您的情况下,check1() 将其传递给modify() 的值硬编码,这就是您尝试模拟modify() 的原因。相反,如果该值是check1() 的参数或Main 类中的可设置字段,则根本不需要使用模拟。

    【讨论】:

    • 我可以只模拟一个类的一个方法,而不是整个类。就像在这种情况下只是 modify() 而不是 check1()?
    • 这就是spy()(或@Spy注释)代替mock()发挥作用的地方。间谍将使用对象的真实实例,并且仅覆盖测试明确表达的行为。
    【解决方案2】:

    您的测试的问题是,您没有使用新创建的 main1 对象。

    如果您想更改被测系统 (SUT) 的行为,您通常会执行以下操作:

    @Test
    public void testCheck1() {
        Test1 main1 = new Main(){
            public int modify(int num) {
                return 5; // hard coded return value
            }
        };
        int num = main1.check1();
        Assert.assertEquals(5, num);
    }
    

    这将创建一个 Main 的子类,其中包含修改方法的新实现。 这是替换 SUT 中难以测试的方法的重要技术。您通常会使用它来避免昂贵的远程调用或类似情况。

    当然可以像这样使用 Mockito 间谍:

    @Test
    public void testCheck1() {
        Test1 main1 = spy(new Main());
        stub(main1.modify(10)).toReturn(5);
    
        int num = main1.check1();
        Assert.assertEquals(5, num);
    }
    

    【讨论】:

    • 根据 Mockito 的作者的说法,stub() 被认为比使用 when() 更易读(相关的 cmets 在上面的 Javadoc 链接中)。但它并没有被弃用,所以我想它是可以的。此外,如果用户已经可以使用 Mockito,那么使用 spy 似乎比声明内部类来部分覆盖行为要清晰得多。
    • 我同意使用 when() 而不是 stub() - 这似乎主要是语义上的差异。
    • 但我更喜欢内部类来修改我的 SUT,因为它们在大多数现实世界场景中更灵活,对 SUT 的影响最小。简而言之:我对我喜欢隔离的依赖对象使用模拟;我使用内部类来组成 SUT 以使用模拟并替换一些方法。
    • 我对 Mockito 很陌生,不幸的是只有少数我工作过的公司使用单元测试或任何类型的测试。如果我的问题听起来很愚蠢,我深表歉意,但我想知道测试您提供结果的方法的目的是什么?在我看来,int a = 5;断言.assertEquals(5, a);不会测试你的真实方法。请指教。
    【解决方案3】:

    虽然我迟到了,但它可能对某些人有用。只是添加到@VivaceVivo 答案:使用间谍时,请考虑 doReturn|Answer|Throw() 存根方法系列。有时使用 when(Object) 来存根间谍是不可能或不切实际的。更多信息here

    【讨论】:

      猜你喜欢
      • 2015-12-13
      • 2023-03-20
      • 1970-01-01
      • 2014-08-30
      • 2020-12-08
      • 2014-02-02
      • 1970-01-01
      • 2023-03-30
      相关资源
      最近更新 更多