代码之家  ›  专栏  ›  技术社区  ›  sbrown

如何Whitebox实例化抽象类,以便用测试逻辑替换方法,在那里我可以操作参数

  •  0
  • sbrown  · 技术社区  · 7 年前

    前言( 请阅读 ):

    • 我不能重构 没有国会法案(但我有消息来源)。
    • 很多抽象类 (及其各种实现)
    • 嘲笑是不够的
    • 我知道 我可以延长 这些抽象类在我的测试包中,并为我想要替换的方法提供了实现。然而 就是看看是否有办法解决这个问题,因为对于拥有大量抽象方法的抽象类(想想NIO的SocketChannel),这是大量无用的样板代码,使得单元测试无法读取。
    • 遗留代码设计不当 这不是编写干净、设计良好的单元测试的方式;这是毫无疑问的。只需知道遗留代码是不容易更改的。

    问题是 )没有从PowerMock得到一个异常,即该类无法实例化,因为它是抽象的:

    @PrepareForTest({SocketChannel.class})
    @RunWith(PowerMockRunner.class)
    public class TestThatUsesSocketChannelChannel
    {
        replace(method(SocketChannel.class, "read", ByteBuffer.class)).with((proxy, method, args) -> 
        {
            // Line below intercepts the argument and manipulates it 
            ((ByteBuffer) args[0]).clear();
        });
        // The line below throws an exception (because SocketChannel is abstract)
        SocketChannel socketChannel = Whitebox.newInstance(SocketChannel.class);
        // Once here, ideally I can continue my test
    }
    
    2 回复  |  直到 7 年前
        1
  •  0
  •   Timothy Truckle Vincent Boutot    7 年前

    您可以使用普通Mockito简单地模拟抽象类:

    AbstractClass theMock = mock(AbstractClass.class);
    

    然后通过PowerMock注入抽象类的模拟。

        2
  •  0
  •   sbrown    7 年前

    :因为SocketChannel是一个抽象类,所以要使用Whitebox。例如,需要对ConcreteClassGenerator进行初步操作。这将创建具有空方法实现的SocketChannel的运行时子级。注意,在此之前,我替换了我所关心的方法。结论:这使我能够实例化(动态)抽象类的子类,而无需显式扩展它。请参阅上面的代码,该代码现在用于此类中间步骤:

    @PrepareForTest({SocketChannel.class})
    @RunWith(PowerMockRunner.class)
    public class TestThatUsesSocketChannelChannel
    {
        replace(method(SocketChannel.class, "read", ByteBuffer.class)).with((proxy, method, args) -> 
        {
            // Line below intercepts the argument and manipulates it 
            ((ByteBuffer) args[0]).clear();
        });
        // THIS Fixes it: generate a an on-the-fly class that implements stub methods
        // for the ones that are abstract and then (and only then) pass that to Whitebox
        Class<?> concreteSocketChannelClass = new ConcreteClassGenerator().createConcreteSubClass(SocketChannel.class);
        SocketChannel socketChannel = (SocketChannel) Whitebox.newInstance(concreteSocketChannelClass);
        // Once here, ideally I can continue my test
    }