【问题标题】:Mockito testing class with dependency created in constructor具有在构造函数中创建的依赖项的 Mockito 测试类
【发布时间】:2017-08-08 09:59:37
【问题描述】:

我正在尝试为看起来像这样的遗留代码编写测试:

public class TestedClass {
    private A a = StaticFactory.createA();
    private B b = StaticFactory.createB();
    // No default constructor
    // code using A and B classes
}

就我对 Mockito 的理解而言,我知道我不能模拟静态方法,但是我知道我可以使用一个小技巧并将这个对象的创建外部化为这样的封装私有方法:

public class TestedClass {

    private A a;
    private B b;

    TestedClass() {
        a = createA();
        b = createB();
    }

    A createA() {
        return StaticFactory.createA();
    }

    B createB() {
        return StaticFactory.createB();
    }

    // code using A and B classes
}

但是使用这个结构我无法创建TestedClass 的间谍,它必须已经是一个间谍才能使用doReturn(..) 结构所以这样的测试不会起作用:

public class TestedClassTest {

    TestedClass testedClass;

    A mockA;
    B mockB;

    @Before
    public void setUp() {
        mockA = mock(A.class);
        mockB = mock(B.class);

        testedClass = Mockito.spy(new TestedClass());

        doReturn(mockA).when(testedClass).createA();
        doReturn(mockB).when(testedClass).createB();   
    }
}

有没有其他方法可以改变 createAcreateB 方法的行为,这些方法在构造函数中被触发以确保我有模拟实例?

在这种情况下,StaticFactory.createA() 运行并抛出异常(正在测试中),无法完成测试的初始化。

Running pl.tbawor.TestedClassTest
Tests run: 1, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.168 sec <<< FAILURE!
shouldTestSomething(pl.tbawor.TestedClassTest)  Time elapsed: 0.114 sec  <<< ERROR!
java.lang.NullPointerException
        at pl.tbawor.StaticFactory.createA(TestedClass.java:32)
        at pl.tbawor.TestedClass.createA(TestedClass.java:14)
        at pl.tbawor.TestedClass.<init>(TestedClass.java:9)
        at pl.tbawor.TestedClassTest.setUp(TestedClassTest.java:26)

我的目标是避免运行创建 AB 对象的原始方法,以防止抛出异常。

由于这是遗留项目,我无法添加任何其他库 (PowerMock)。

我会补充一点,TestingClass 修改应该尽可能少。

【问题讨论】:

标签: java unit-testing junit mockito


【解决方案1】:

您必须在间谍实例上记录一个行为,但您必须在创建间谍之前完成。

这里:

doReturn(mockA).when(testedClass).createA();
doReturn(mockB).when(testedClass).createB();
testedClass = Mockito.spy(new TestedClass());

当您记录模拟行为时,testedClassnull

颠倒这些语句的顺序:

testedClass = Mockito.spy(new TestedClass());
doReturn(mockA).when(testedClass).createA();
doReturn(mockB).when(testedClass).createB();

此外,此解决方案不会解决您的根本问题,因为调用的无参数构造函数仍然依赖于您想要隔离但目前还没有的依赖项:

TestedClass() {
    a = createA();
    b = createB();
}

您应该重构一点 TestedClass 以允许设置依赖项。
例如,您可以重载构造函数:

TestedClass(A a, B b) {
    this.a = a;
    this.b = b;
}

通过这种方式,您可以模拟 TestedClass 的依赖关系:

@Before
public void setUp() {
    mockA = Mockito.mock(A.class);
    mockB = Mockito.mock(B.class);
    testedClass = new TestedClass(mockA, mockB);
}

【讨论】:

  • 抱歉,添加了错误的示例。正如我编辑的问题是,当我使用这种构造时,运行原始方法 od TestClass,在单元测试中这导致 NullPointerException(内部有一些状态)无法完成间谍的初始化。
  • 好吧。您应该在问题中添加 stracktrace 以及执行的测试。了解问题可能会有所帮助。
  • 已经添加了示例,我无法添加真正的异常,但这应该显示我正在尝试做的事情。
  • 我更新了 API 的一些小改动。它不应该对遗留代码造成很大的干扰。
  • 我不建议您更改签名,而是重载它(与实际签名一起使用的新构造函数)。添加新的构造函数/方法不会破坏客户端代码。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-03
  • 2020-10-24
  • 2019-03-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多