信息很明显:你不能模拟不可见的和最终的课程。简短的回答:创建一个匿名类的命名类,然后测试这个类!
长答案,让我们挖掘原因!
匿名类是最终的
您实例化一个匿名类FilterFactory
,当编译器看到一个匿名类时,它会创建一个final和包可见类。所以匿名类不能通过标准方法模拟,即通过 Mockito。
模拟匿名类:可能但如果不是 HACKY 则很脆弱
好的,现在假设您希望能够通过 Powermock 模拟这个匿名类。当前编译器使用以下方案编译匿名类:
Declaring class + $ + <order of declaration starting with 1>
模拟匿名类可能但很脆弱(我是认真的)所以假设匿名类是第 11 个要声明的类,它将显示为
InputHelper$11.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。
此外,匿名类命名实际上取决于声明的顺序这一事实是一个问题,当有人之前添加另一个匿名类时,编号可能会改变。匿名类是为了保持匿名,如果编译器决定有一天使用字母甚至随机标识符怎么办!
因此,通过 Powermock 模拟匿名类是可能的,但很脆弱,千万不要在实际项目中这样做!
编辑注: Eclipse 编译器有不同的编号方案,它总是使用 3 位数字:
Declaring class + $ + <pad with 0> + <order of declaration starting with 1>
此外,我认为 JLS 没有明确指定编译器应如何命名匿名类。
您不会将间谍重新分配给静态字段
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
. 所以你需要通过反射来实际设置这个字段。您可以使用Whitebox
Powermock 提供的实用程序。
结论
仅使用匿名过滤器使用BufferedInputStream
.
选择
我宁愿写下面的代码:
将使用命名类的输入助手,我不使用接口名称向用户说明此过滤器的意图是什么!
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);
}
}
现在你可以写一个这样的测试:
@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]);
}
}
但是如果你强制CBZip2InputStream
只接受这个测试场景,最终可能会避免 powermock 的东西BufferedInputStream
。通常使用 Powermock 意味着设计有问题。在我看来,Powermock 非常适合遗留软件,但在设计新代码时会使开发人员失明;由于他们错过了 OOP 的优点,我什至会说他们正在设计遗留代码。
希望有帮助!