【问题标题】:Is it possible to invoke mocked object's method?是否可以调用模拟对象的方法?
【发布时间】:2014-07-07 20:10:00
【问题描述】:

例如,我有这个类:

public class A {
    private List<String> list;

    public A(B b){
        list = b.getList();
    }

    public List<String> someMethod(){
        return list;
    }
}

我想在不调用构造函数的情况下对someMethod 进行单元测试。我使用 reflection 来设置list

问题是我不想创建 B 类对象,我不能模拟它,因为它会导致 NPE。

所以我的问题是:

如何在不调用A 的构造函数的情况下测试someMethod?有什么方法可以模拟 A 类并且不会失去调用方法的可能性?

用零参数创建构造函数不是解决方案。

注意:不想更改 A 类的任何部分。我在问是否可以在不添加或更改 A 类中的任何内容的情况下执行此测试。

【问题讨论】:

    标签: java unit-testing reflection mockito


    【解决方案1】:

    您可以在不调用 Mockito 的构造函数的情况下测试 A 类。不确定我是否真的理解您的要求,但以下代码对我有用。

    import org.junit.Test;
    import org.springframework.test.util.ReflectionTestUtils;
    
    import java.util.ArrayList;
    import java.util.List;
    
    import static org.hamcrest.CoreMatchers.is;
    import static org.junit.Assert.assertThat;
    import static org.mockito.Mockito.mock;
    import static org.mockito.Mockito.when;
    
    public class ATest {
    
        @Test
        public void test() {
            A a = mock(A.class);
            when(a.someMethod()).thenCallRealMethod();
            List<String> listInA = new ArrayList<String>();
            ReflectionTestUtils.setField(a, "list", listInA);
            assertThat(a.someMethod(), is(listInA));
        }
    }
    

    【讨论】:

    • 我印象深刻。这正是我想要的。谢谢。
    【解决方案2】:

    你应该模拟你的类的协作者——这意味着你可以创建一个正在测试的类的实例,并传入模拟,配置为在调用它的方法时“做正确的事情”。

    在您的示例中,您想创建一个模拟 B,并像这样使用它:

    @RunWith(MockitoJUnitRunner.class)
    class myTest {
      @Mock private B b;
    
      public void someMethod() {
        doReturn(new ArrayList<String>()).when(b).getList();
        A a = new A(b);
        assertEquals("result", a.someMethod().get(0));
      }
    }
    

    【讨论】:

    • 您可能需要实际返回数组列表中的某些内容,具体取决于 A 的进一步行为,但鉴于您的示例,这应该可行。
    • 问题是我根本不想模拟B。真正的A 构造函数正在使用来自 B 的多个方法及其文件。它们用于设置A 字段,所以我不想模拟B 中的所有内容。我想我无法实现其他任何目标。你的答案当然是正确的。
    • 如果正确,最好打勾。现在,如果你愿意,你总是可以创建一个 B 的实例作为间谍(使用@Spy)——这将允许你只模拟你需要的方法。然而,在编写测试时这是一个经常出现的问题——要编写单元测试你/必须/模拟/存根协作者,否则你的测试是脆弱的。
    【解决方案3】:

    我不想创建 B 类对象

    添加不需要 B 的构造函数或工厂方法。

    public A(B b){
        this(b.getList());
    }
    
    /* package local */ A(List<String> list){
        this.list = list;
    }
    

    通过将构造函数包设为本地,它可以被同一包中的单元测试访问。

    如何在不调用 A 的构造函数的情况下测试 someMethod?

    你可以使用

    A a = theUnsafe.allocateInstance(A.class);
    

    但除非您没有其他选择,否则不建议这样做,例如反序列化。

    【讨论】:

    • 在任何情况下,您都不应该将代码写入仅用于测试的生产类中。您应该在类似生产的场景中测试您的类,并且您应该只检查类似生产的代码路径。
    • 用零参数创建构造函数不是解决方案。我猜 OP 对这种方法也有同样的感觉。
    • @EngineerDollery +1 我同意,这是理想的,但并非所有项目都是理想的。
    猜你喜欢
    • 2012-02-14
    • 2019-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-25
    相关资源
    最近更新 更多