Mockito测试类,在构造函数中创建依赖项

Tom*_*wor 2 java junit unit-testing mockito

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

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

至于我对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
}
Run Code Online (Sandbox Code Playgroud)

但是使用这种结构我无法创建间谍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();   
    }
}
Run Code Online (Sandbox Code Playgroud)

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

在这种情况下,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)
Run Code Online (Sandbox Code Playgroud)

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

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

我将补充说,TestingClass修改应该尽可能少.

dav*_*xxx 5

您必须在间谍实例上记录行为,但在创建间谍之前执行此操作.

这里 :

doReturn(mockA).when(testedClass).createA();
doReturn(mockB).when(testedClass).createB();
testedClass = Mockito.spy(new TestedClass());
Run Code Online (Sandbox Code Playgroud)

testedClassnull 你记录模拟行为的时候.

颠倒这些陈述的顺序:

testedClass = Mockito.spy(new TestedClass());
doReturn(mockA).when(testedClass).createA();
doReturn(mockB).when(testedClass).createB();
Run Code Online (Sandbox Code Playgroud)

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

TestedClass() {
    a = createA();
    b = createB();
}
Run Code Online (Sandbox Code Playgroud)

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

TestedClass(A a, B b) {
    this.a = a;
    this.b = b;
}
Run Code Online (Sandbox Code Playgroud)

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

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

  • 我不建议你改变签名,但要重载它(一个与实际生成器一起使用的新构造函数).添加新的构造函数/方法不会破坏客户端代码. (2认同)