【问题标题】:Mocking private method of class under test using JMockit使用 JMockit 模拟被测类的私有方法
【发布时间】:2014-08-17 03:20:01
【问题描述】:

我想模拟一个被测类的私有方法,但是当方法被调用时,方法前两次返回 false,之后它应该返回 false。 这是我尝试过的代码。 这是正在测试的类

public class ClassToTest 
{
    public void methodToTest()
    {
        Integer integerInstance = new Integer(0);
        boolean returnValue= methodToMock(integerInstance);
        if(returnValue)
        {
            System.out.println("methodToMock returned true");
        }
        else
        {
            System.out.println("methodToMock returned true");
        }
        System.out.println();
    }
    private boolean methodToMock(int value)
    {
        return true;
    }
}

测试类

import org.junit.Test;
import static mockit.Deencapsulation.*;

import mockit.*;
public class TestAClass 
{
    @Tested ClassToTest classToTestInstance;
    @Test
    public void test1()
    {

        new NonStrictExpectations(classToTestInstance)
        {
            {
                invoke(classToTestInstance, "methodToMock", anyInt);
                returns(false);
                times = 2;

                invoke(classToTestInstance, "methodToMock", anyInt);
                returns(true);
                times = 1;

            }
        };

        classToTestInstance.methodToTest();
        classToTestInstance.methodToTest();
        classToTestInstance.methodToTest();

    }
}

我这样做是为了得到想要的结果

    final StringBuffer count = new StringBuffer("0");
    new NonStrictExpectations(classToTestInstance)
    {

        {
            invoke(classToTestInstance, "methodToMock", anyInt);
            result= new Delegate() 
            {
                boolean methodToMock(int value)
                {                   
                    count.replace(0, count.length(), Integer.valueOf(Integer.valueOf(count.toString())+1).toString());
                    if(Integer.valueOf(count.toString())==3)
                    {
                        return true;
                    }
                    return false;
                }
            };

        }
    };

【问题讨论】:

  • 如果你想模拟一个私有方法,这听起来很像你应该将该方法的功能移动到另一个类。
  • 这是被测类的私有方法。
  • 而且私有方法不应该被测试?
  • 我正在测试一个公共方法并想模拟私有方法。
  • 我不认为这个问题应该被关闭,因为它引用的“重复”与另一个问题有关 - 如何测试私有方法。这个问题清楚地表明了模拟私有方法的要求,但是对公共方法的测试,其中被测类正在模拟私有方法

标签: java unit-testing junit mocking jmockit


【解决方案1】:

使用期望(或 StrictExpectations)

结合使用 Expectations 和 Deencapsulation.invoke(),您可以部分模拟被测对象:

import org.junit.Test;
import static mockit.Deencapsulation.*;
import mockit.*;

public class TestAClass
{
    public static class ClassToTest 
    {
        public void methodToTest()
        {
            boolean returnValue = methodToMock(0);
            System.out.println("methodToMock returned " + returnValue);
        }

        private boolean methodToMock(int value) { return true; }
    }

    @Tested ClassToTest classToTestInstance;

    @Test
    public void partiallyMockTestedClass() {
        new Expectations(classToTestInstance) {{
            invoke(classToTestInstance, "methodToMock", anyInt);
            result = false;
            times = 2;
        }};

        classToTestInstance.methodToTest();
        classToTestInstance.methodToTest();
        classToTestInstance.methodToTest();
    }
}

上面的测试打印出来:

methodToMock returned false
methodToMock returned false
methodToMock returned true

一般来说,当然,我们应该避免模拟private 方法。也就是说,我在实践中发现这样做有时很有用,通常是当你有一个私有方法做一些不平凡的事情并且已经被另一个测试测试过时;在这种情况下,在第二次测试中模拟该私有方法(对于不同的公共方法或通过同一公共方法的不同路径)可能比设置必要的输入/条件容易得多。

使用 NonStrictExpectations(在 JMockit 1.23 中已弃用)

使用 NonStrictExpectations 编写测试同样容易(OP 最初的尝试不起作用,只是因为相同的非严格期望被记录两次,第二次记录覆盖了第一):

@Test
public void partiallyMockTestedClass() {
    new NonStrictExpectations(classToTestInstance) {{
        invoke(classToTestInstance, "methodToMock", anyInt);
        returns(false, false, true);
    }};

    classToTestInstance.methodToTest();
    classToTestInstance.methodToTest();
    classToTestInstance.methodToTest();
}

使用委托

如果需要更大的灵活性,我们可以随时记录基于Delegate 的结果:

@Test
public void partiallyMockTestedClass() {
    new NonStrictExpectations(classToTestInstance) {{
        invoke(classToTestInstance, "methodToMock", anyInt);

        result = new Delegate() {
           boolean delegate() {
               boolean nextValue = ...choose next return value somehow...
               return nextValue;
           }
        }
    }};

    classToTestInstance.methodToTest();
    classToTestInstance.methodToTest();
    classToTestInstance.methodToTest();
}

【讨论】:

  • 感谢您的支持,新的 Expectations(classToTestInstance) 在实际 mehtod 运行后两次模拟该方法,因为您在我的另一个问题中回答了我,这里我想返回“true”而不是实际值。在methodToMock的定义中,它只是简单地返回“true”,我想在这里问一下methodToMock的定义中是否存在逻辑,之后mehtodToMock返回一些值,我们想从中返回“true”。那我们应该怎么模拟呢?
  • 不确定这是否是您所需要的,但您始终可以记录一个委托对象 (result = new Delegate() { ... };) 以根据一些任意逻辑返回值。
  • 谢谢@Rogério。请提供此评论作为答案,我会接受答案。
  • 所描述的方式不是用于部分模拟,而是仅用于完全模拟。我们不能使用已经存在的类/实例方法和字段。有时它可能有用,但很少。 Kaushik 的答案更有用。
  • NonStrictExpectations 在 JMockit 1.27 中被删除,并且在 JMockit 1.23 中使用 Expectations 模拟私有方法的能力被删除。因此,您现在必须使用 MockUp ,请参阅@Kaushik 答案。
【解决方案2】:

这对我有用:-

        new MockUp<ClassToTest>() {
            @Mock
            boolean methodToMock(int value) {
                return true;
            }
        };

【讨论】:

  • @Gangnus 私有方法模拟(使用@Mocked)自 JMockit 1.23 起已停止使用,并且在版本 1.27 中使用 MockUp 时将受到限制。我的回答是错误的,因为它仍然假设在某些情况下模拟私有方法可能有用;但事实并非如此。
  • 很多人想知道如何模拟私有方法。他们中的 12 人甚至在这里投票了一些答案。至于我,我寻找解决方案,因为我需要它。我找到了。你怎么能说没人需要呢?你的答案的问题是不同的——它太复杂了,显然人们需要一些简单的东西。它声明部分模拟提供了完整的模拟。我相信你是这个主题的了不起的专家。但对我来说,你的解释太复杂了。
  • 我正在使用 jmockit 1.18,这对我有用,而 @Rogério 的答案却没有
  • JMockit 在模拟私有方法方面似乎改变了主意。不管我们个人对此有何看法,也许这应该在主要版本中完成?关于测试私有方法的话题,我认为有很多(遗留)代码需要我们有效地编写表征测试——模拟私有方法是一个非常有用的特性。我认为 JMockit 是处理这些情况的首选工具 - 任何东西都可以被模拟,不管当前编程最佳实践的概念如何。
  • 查看 JMockit issue 326 上的讨论,了解更多关于测试原理等的详细信息哈哈哈
【解决方案3】:

在这里,您可以使用模拟行为覆盖测试类的特定方法。

以下代码:

public class ClassToTest 
{
    public void methodToTest()
    {
        Integer integerInstance = new Integer(0);
        boolean returnValue= methodToMock(integerInstance);
        if(returnValue)
        {
            System.out.println("methodToMock returned true");
        }
        else
        {
            System.out.println("methodToMock returned true");
        }
        System.out.println();
    }
    private boolean methodToMock(int value)
    {
        return true;
    }
}

测试类是:

public class ClassToTestTest{

    @Test
    public void testMethodToTest(){

        new Mockup<ClassToTest>(){
            @Mock
            private boolean methodToMock(int value){
                return true;
            }
        };

        ....    

    }
}

【讨论】:

  • 你用的是哪个版本?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多