如何将Whitebox实例化一个抽象类,以便用我可以操作参数的测试逻辑替换方法

时间:2017-09-20 15:48:50

标签: java unit-testing powermock

序言(请阅读):

  • 处理遗留代码我无法重构没有会议行为(但我有来源)。
  • 此遗留代码与许多抽象类(及其各种实现)进行交互
  • 模拟还不够因为我需要拦截发送到抽象方法的方法参数并在替换(测试)逻辑中操作它们
  • 我知道我可以在我的测试包上扩展那些抽象类,并提供我想要替换的方法的实现。然而这个问题的目标是看看它是否有办法,因为在抽象类的情况下有大量的抽象方法(想想来自NIO的SocketChannel),这是一堆无用的样板使单元测试不可读的代码。
  • 我知道遗留代码的设计很差而且这不是编写干净精心设计的单元测试的方法;这不是问题。只要知道Legacy代码不能轻易更改。

问题是:如何实现这一点(使用 PowerMock )而不会从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 个答案:

答案 0 :(得分:0)

您可以使用简单的Mockito来模拟抽象类:

AbstractClass theMock = mock(AbstractClass.class);

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

答案 1 :(得分:0)

找到答案:因为SocketChannel是一个抽象类,为了使用Whitebox.newInstance,需要使用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
}