【问题标题】:How to call a private constructor of an abstract class for mocking.如何调用抽象类的私有构造函数进行模拟。
【发布时间】:2014-07-23 23:13:27
【问题描述】:

我遇到了一些关于如何正确测试此代码的问题。我希望能够使用 Mocking 框架或即使反射来模拟对 delegateForFoo 的调用。但是,当我尝试通过反射或 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;
    }
}

【问题讨论】:

    标签: java unit-testing reflection easymock powermock


    【解决方案1】:

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

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

    【讨论】:

    • 是的,我需要一个类来扩展 foo 是正确的,但是我可以通过什么方式扩展 Foo 并更改 Foo 的私有构造函数以获取我在测试?
    【解决方案2】:

    正如其他人所指出的,由于类的抽象性,您将无法直接使用 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: Mocking a Constructor documentation 以了解有关使用 PowerMock 模拟构造函数的更多信息。

    【讨论】:

    • 我们对 Foo 功能的测试不应该与 Bar 耦合吗?如果我们摆脱 Bar 并拥有不同的子类会怎样。这将需要更改看起来不正确的 Foo 测试。
    • 再次,您又回到了其他回答者指出的问题。为了测试一个抽象类的功能,你需要一个它的实现。该实现可以是任何东西。我在回答中使用了 Bar 类,因为您在问题中提供了它。您总是可以拥有 Foo 类 (FooTestImpl) 的测试实现,它使您能够实例化它并访问它的方法。然后你可以检查对代表的调用。
    【解决方案3】:

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

    【讨论】:

      猜你喜欢
      • 2022-01-22
      • 2012-04-28
      • 2012-07-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-18
      • 1970-01-01
      相关资源
      最近更新 更多