如何调用抽象类的私有构造函数进行模拟



我在如何正确测试这段代码时遇到了一些问题。我希望能够模拟调用delegateForFoo使用一个mock框架,或者即使反射。但是,当我尝试通过反射或PowerMock来执行此操作时,我得到了如下面的代码所示的错误。

@Test
public void testDelegatedMethodWithPowerMock() throws Exception {
    DelegateForFoo mockedDelegateForFoo = PowerMock
            .createMock(DelegateForFoo.class);
    Foo foo = Whitebox.invokeConstructor(Foo.class, mockedDelegateForFoo);
    // org.powermock.reflect.exceptions.ConstructorNotFoundException: Failed
    // to find a constructor with parameter types: at line above.
    Constructor<Foo> fooConstructor = Foo.class
            .getDeclaredConstructor(DelegateForFoo.class);
    fooConstructor.setAccessible(true);
    Foo foo2 = fooConstructor.newInstance(mockedDelegateForFoo);
    // java.lang.InstantiationException at line above.
}

我尽我最大的努力检查以前的stackOverflow问题,我没有看到一个直接解决这个问题。有一些关于如何调用具体类的私有构造函数的答案,以及一些关于如何在抽象类上对私有方法进行分类的答案,但没有针对这个特定实例。

我的正式问题如下:是否有一种方法可以调用抽象类的私有构造函数并传入模拟对象以进行测试。

我知道通常你不应该测试私有方法,等等。然而,在这种情况下,我想测试一个构造函数,它有一个合法的理由是私有的。没有理由让Bar知道任何关于委托的事情,为了适当的封装,它应该是私有的。提前感谢。

我使用的代码如下所示。

public abstract class Foo {
    private DelegateForFoo delegateForFoo;
    private int valueToBeSetFromDelegate;
    Foo() {
        // I don't want the subclasses to do anything with this DelegateForFoo.
        // It's set through the constructor because there is a requirement that
        // Foo must have a delegateForFoo in order to be created.
        this(new DelegateForFoo());
    }
    // This Constructor is indirectly called by the subclasses. They don't know
    // anything about how it is set, or what it does.
    private Foo(DelegateForFoo delegateForFoo) {
        this.delegateForFoo = delegateForFoo;
        this.valueToBeSetFromDelegate = this.delegateForFoo.delegatedMethod();
    }
    int useTheDelegateForSomeMethod() {
        return this.delegateForFoo.delegatedMethod();
    }
}

public class DelegateForFoo {
    int delegatedMethod() {
        return 5;
    }
}

public class Bar extends Foo {
    // here, Bar doesn't care about the delegate for Foo at all. It only cares
    // that the method it is called has some implementation.
    int useDelegateForFooWithoutKnowingAboutIt() {
        return super.useTheDelegateForSomeMethod() + 10;
    }
}

不能创建Foo类的实例的原因是它是abstract。Java中没有任何机制可以创建抽象类的实例。

要测试抽象类,你需要定义一个扩展Foo的新类。这意味着将调用默认构造函数。

正如其他人指出的那样,由于类的抽象性,您将无法直接使用Foo构造器类做任何事情。

已经说过,您应该能够为Bar类编写一个测试,如果您考虑它在一个稍微不同的方式。不模拟Foo构造函数调用,而是模拟DelegateForFoo构造函数调用。

我想我会这样做:

  • 使用PowerMock准备Foo, Bar和DelegateForFoo类。
  • 为DelegateForFoo构造函数调用设置一个PowerMock期望,并返回一个Mock对象。
  • 添加对delegatedMethod的期望
  • 创建一个Bar对象然后调用useDelegateForFooWithoutKnowingAboutIt方法
  • 不要忘记验证。

所以它看起来像这样:(我为任何拼写错误道歉,我没有在IDE中尝试过)

@RunWith( PowerMockRunner.class )
@PrepareForTest( Foo.class, Bar.class, DelegateForFoo.class )
public class BarTest
    @Test
    public void thatDelegateForFooCanBeMocked() {
        DelegateForFoo mockDelegate = EasyMock.createMock(DelegateForFoo.class);
        EasyMock.expect( mockDelegate.delegatedMethod() ).andReturn( 5 );
        EasyMock.replay( mockDelegate );
        PowerMock.expectNew( DelegateForFoo.class ).andReturn( mockDelegate );
        PowerMock.replayAll();
        Bar bar = new Bar();
        int result = bar. useDelegateForFooWithoutKnowingAboutIt();
        Assert.assertThat( result, CoreMatchers.is(15) );
        PowerMock.verifyAll();
        EasyMock.verify(mockDelegate);
    }
}

当然,文档是一件很棒的事情,所以请参阅PowerMock: mock构造函数文档,以了解更多关于使用PowerMock模拟构造函数的信息。

让子类访问私有成员的唯一方法是拥有一个扩展抽象类的嵌套类。这样,你就有了一个类可以访问私有成员,而其他类不能。

最新更新