绕过方法存根中的构造函数调用

时间:2016-12-23 15:51:39

标签: java unit-testing mockito

我目前正在使用Mockito作为我的java JSP页面的测试框架。我没有足够的经验。但是,有一个问题我无法找到解决方案。

问题是,我想测试一个不可变的类。您只能使用构造函数初始化属性。访问者(getter)可以检索这些值。但是,其中一个setter方法有一个执行另一个类的构造调用的方法。

public class MyImmutableClass {
    private final String foo;
    private final String bar;
    private final int jeez;

    public MyImmutableClass(String f, String b, Integer j) {
        this.foo = setFoo(f);
        // same for other fields
    }

    private String setFoo(String newFoo) {
        String retVal = "";
        List<String> identifiers = new MyDataBaseHandler().getListOfIdentifiers(); // construct call
        if (identifiers.contains(newFoo)) {
            // ...
        }
        else {
            // ...
        }
        return retVal;
    }

    public boolean isValid() {
        // returns the correctness of this instance
    }
}

为了测试这个不可变的类,我已经导入了Mockito并尝试了以下方法。

@RunWith(MockitoJUnitRunner.class)
public class MyImmutableClassTest {

    @Test
    public void validConstruction() throws Exception {
        MyDataBaseHandler myDB = mock(MyDataBaseHandler.class);
        doReturn(this.getIdentifiers()).when(myDB).getListOfIdentifiers();

        MyImmutableClass m = new MyImmutableClass("foo", "bar", 1);
        assertTrue(m.isValid());
    }
}

方法this.getIdentifiers()是一种重现字符串列表的私有方法。 (为了测试起见,只有3个元素放在该列表中)。这是为了确保标识符的验证能够正确地完成工作。

但是,此测试失败。问题是getListOfIdentifiers()在方法体中有一个私有方法调用。这会引发NPE。显然,很难“绕过”getListOfIdentifiers()方法中的构造调用。 callToMyPrivateMethod()与数据库连接。由于单元测试是在本地运行的,因此无法建立数据库连接。因此,我想通过使用Mockito来绕过返回值。

public List<String> getListOfIdentifiers() {
    // ...
    callToMyPrivateMethod(); // throws NPE ...
    // ...
    return list;
}

NPE的报告:

java.lang.NullPointerException
    at package.MyDataBaseHandler.callToMyPrivateMethod(MyDataBaseHandler.java:67)
    at package.MyDataBaseHandler.getListOfIdentifiers(MyDataBaseHandler.java:25)

该行实际上是一个语句,它在数据库连接上创建一个准备好的语句(它本身不是null ... keh)。准备好的声明引发了NPE。将mock(...)替换为spy(new MyDataBase()) ofc不起作用,因为它与正在构造的实例不同。

我也尝试了@InjectMock这件事,但这无助于解决我的问题。可能是我没有正确实现注入事项(文档不是那么清楚)。

有没有办法解决这个问题,以便Mockito框架拦截对getListOfIdentifiers()的调用,然后返回到我创建的列表?或者我是否正在盯着XY问题?

1 个答案:

答案 0 :(得分:0)

您在测试方法中创建new MyDataBaseHandler()的实例。

new MyDataBaseHandler()是您所测试的类与之通信的依赖项。你正确地认识到了,因为你试图模仿它。

但是根据您当前的实施情况,您无法做到。

有两种解决方案。

  1. 使用PowerMock(ito) 这样您就可以将调用重定向到new MyDataBaseHandler()以返回MyDataBaseHandler类的模拟 但我个人认为使用PowerMock是对糟糕设计的投降。

  2. 改善您的设计。 恕我直言在类中创建(直接)依赖项的实例违反了单一责任模式。依赖关系的实例化根本不是一个类的责任。

    您应该在MyDataBaseHandler之外创建MyImmutableClass的实例,并将其注入MyImmutableClass个对象,最好通过构造函数。

    然后,您可以轻松地将此依赖项替换为您已创建的模拟。