在匿名类中测试方法时,如何使用Powermockito来模拟新对象的构造? [英] How do I use Powermockito to mock the construction of new objects when testing a method in an anonymous class?

查看:1502
本文介绍了在匿名类中测试方法时,如何使用Powermockito来模拟新对象的构造?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想写一个JUnit测试来验证下面的代码使用BufferedInputStream:

I woud like to write a JUnit test to verify that the code below uses a BufferedInputStream:

public static final FilterFactory BZIP2_FACTORY = new FilterFactory() {
    public InputStream makeFilter(InputStream in) {        
        // a lot of other code removed for clarity 
        BufferedInputStream buffer = new BufferedInputStream(in);
        return new CBZip2InputStream(buffer);
    }
};

(FilterFactory是一个接口。)

(FilterFactory is an interface.)

我到目前为止的测试看起来像这样:

My test thus far looks like this:

@Test
public void testBZIP2_FactoryUsesBufferedInputStream() throws Throwable {
    InputStream in = mock(InputStream.class);
    BufferedInputStream buffer = mock(BufferedInputStream.class);
    CBZip2InputStream expected = mock(CBZip2InputStream.class);

    PowerMockito.spy(InputHelper.BZIP2_FACTORY);  // This line fails
    whenNew(BufferedInputStream.class).withArguments(in).thenReturn(buffer);
    whenNew(CBZip2InputStream.class).withArguments(buffer).thenReturn(expected);
    InputStream observed = InputHelper.BZIP2_FACTORY.makeFilter(in);

    assertEquals(expected, observed);
}

对PowerMockito.spy的调用会引发此消息的异常:

The call to PowerMockito.spy raises an exception with this message:

org.mockito.exceptions.base.MockitoException: 
Mockito cannot mock this class: class edu.gvsu.cis.kurmasz.io.InputHelper$1
Mockito can only mock visible & non-final classes.

我应该使用什么代替PowerMocktio.spy设置对whenNew的调用?

What should I be using instead of PowerMocktio.spy to set up the calls to whenNew?

推荐答案

这个消息很明显:你不能模拟不可见的和最终的类。简短的回答:创建一个匿名的命名类,然后测试此类

The message is pretty obvious: You can't mock non-visible and final classes. Short answer : Create a named class of your anonymous one, and test this class instead!

答案很长,让我们挖掘原因!

Long answer, let's dig why !

您实例化一个 FilterFactory ,当编译器看到匿名类时,它会创建最终包可见类。所以匿名类不能通过标准均值来模拟,即通过Mockito。

You instantiate an anonymous class of FilterFactory, when the compiler sees an anonymous class, it creates a final and package visible class. So the anonymous class is not mockable through standard mean i.e. through Mockito.

好的,现在假设您希望能够通过Powermock模拟这个匿名类。当前编译器使用以下方案编译匿名类:

OK, now suppose you want to be able to mock this anonymous class through Powermock. Current compilers compile anonymous class with following scheme :

Declaring class + $ + <order of declaration starting with 1>

嘲弄匿名类可能但很脆弱(我的意思是)
所以假设匿名类是第11个被宣布,它将显示为

Mocking anonymous class possible but brittle (And I mean it) So supposing the anonymous class is the eleventh to be declared, it will appear as

InputHelper$11.class

因此,您可能准备测试匿名类:

So you could potentially prepare for test the anonymous class:

@RunWith(PowerMockRunner.class)
@PrepareForTest({InputHelper$11.class})
public class InputHelperTest {
    @Test
    public void anonymous_class_mocking works() throws Throwable {
        PowerMockito.spy(InputHelper.BZIP2_FACTORY);  // This line fails
    }
}

此代码将编译,但是最终将报告为IDE的错误。 IDE可能不知道 InputHelper $ 11.class 。 IntelliJ谁不使用编译类来检查代码报告。

This code will compile, BUT will eventually be reported as an error with your IDE. The IDE probably doesn't know about InputHelper$11.class. IntelliJ who doesn't use compiled class to check the code report so.

此外,匿名类命名实际上取决于声明的顺序是一个问题,当有人之前添加另一个匿名类时,编号可能会改变。
匿名类是保持匿名的,如果编译人员决定有一天使用字母甚至是随机标识符会怎样!

Also the fact that the anonymous class naming actually depends on the order of the declaration is a problem, when someone adds another anonymous class before, the numbering could change. Anonymous classes are made to stay anonymous, what if the compiler guys decide one day to use letters or even random identifiers!

所以嘲笑匿名类通过Powermock可能但很脆弱,不要在真正的项目中做到这一点!

编辑注: Eclipse编译器有一个不同的编号方案,它总是使用一个3位数字:

EDITED NOTE : The Eclipse compiler has a different numbering scheme, it always uses a 3 digit number :

Declaring class + $ + <pad with 0> + <order of declaration starting with 1>

此外,我认为JLS没有明确说明编译器应如何命名匿名类。

Also I don't think the JLS clearly specify how the compilers should name anonymous classes.

PowerMockito.spy(InputHelper.BZIP2_FACTORY);  // This line fails
whenNew(BufferedInputStream.class).withArguments(in).thenReturn(buffer);
whenNew(CBZip2InputStream.class).withArguments(buffer).thenReturn(expected);
InputStream observed = InputHelper.BZIP2_FACTORY.makeFilter(in);

PowerMockito.spy 返回间谍,它不会更改 InputHelper.BZIP2_FACTORY 的值。所以你需要通过反射这个字段来实际设置。您可以使用Powermock提供的 Whitebox 实用程序。

PowerMockito.spy returns the spy, it doesn't change the value of InputHelper.BZIP2_FACTORY. So you would need to actually set via reflection this field. You can use the Whiteboxutility that Powermock provide.

使用mocks测试匿名过滤器使用 BufferedInputStream 。太麻烦了。

Too much trouble to just test with mocks that the anonymous filter uses a BufferedInputStream.

我宁愿写下面的代码:

将使用命名类的输入助手,我不喜欢使用接口名称向用户说明此过滤器的意图是什么!

An input helper that will use the named class, I don't use the interface name to make clear to the user what is the intent of this filter!

public class InputHelper {
    public static final BufferedBZIP2FilterFactory BZIP2_FACTORY = new BufferedBZIP2FilterFactory();
}

现在过滤器本身:

public class BufferedBZIP2FilterFactory {
    public InputStream makeFilter(InputStream in) {
        BufferedInputStream buffer = new BufferedInputStream(in);
        return new CBZip2InputStream(buffer);
    }
}

现在你可以写一个这样的测试:

Now you can write a test like this :

@RunWith(PowerMockRunner.class)
public class BufferedBZIP2FilterFactoryTest {

    @Test
    @PrepareForTest({BufferedBZIP2FilterFactory.class})
    public void wraps_InputStream_in_BufferedInputStream() throws Exception {
        whenNew(CBZip2InputStream.class).withArguments(isA(BufferedInputStream.class))
                .thenReturn(Mockito.mock(CBZip2InputStream.class));

        new BufferedBZIP2FilterFactory().makeFilter(anInputStream());

        verifyNew(CBZip2InputStream.class).withArguments(isA(BufferedInputStream.class));
    }

    private ByteArrayInputStream anInputStream() {
        return new ByteArrayInputStream(new byte[10]);
    }
}

但最终可以避免这个测试场景中的powermock东西你强制 CBZip2InputStream 只接受 BufferedInputStream 。通常使用Powermock意味着设计出了问题。 在我看来,Powermock非常适合遗留软件,但在设计新代码时可能会使开发人员失明;因为他们忽略了OOP的优点,我甚至会说他们正在设计遗留代码。

But could eventually avoid powermock stuff for this test scenario if you force the CBZip2InputStream to only accept BufferedInputStream. Usually using Powermock means something is wrong with the design. In my opinion Powermock is great for legacy softwares, but can blind developers when designing new code; as they are missing the point of OOP's good part, I would even say they are designing legacy code.

希望有所帮助!

这篇关于在匿名类中测试方法时,如何使用Powermockito来模拟新对象的构造?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆