当前位置: 首页 > 面试题库 >

在匿名类中测试方法时,如何使用Powermockito模拟新对象的构造?

晏卓君
2023-03-14
问题内容

我想编写一个JUnit测试来验证以下代码是否使用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是一个接口。)

到目前为止,我的测试如下:

@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会引发以下消息异常:

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的调用?


问题答案:

信息很明显:您不能模拟不可见和最终类。简短答案: 创建一个 匿名 命名类 ,然后 测试该类

长答案,让我们找出原因吧!

匿名班是最终的

您实例化一个的匿名类FilterFactory,当编译器看到一个匿名类时,它将创建一个 最终的 ,可 打包的可见
类。因此,匿名类无法通过标准方式(即通过Mockito)进行嘲笑。

模拟匿名课程:可能,但如果不是HACKY,则为BRITTLE

好的,现在假设您希望能够通过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。因此,您将需要通过反射来实际设置此字段。您可以使用WhiteboxPowermock提供的实用程序

结论

仅用匿名过滤器使用的模拟进行测试就很麻烦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的优势,所以我什至会说他们正在设计遗留代码。

希望能有所帮助!



 类似资料:
  • 问题内容: 考虑以下(简化的)枚举: 可以在以下函数中使用它: 我现在想为此编写一个单元测试,以模拟每个枚举实例中对myMethod()的调用。我尝试了以下方法: 但是,真正的实现和正在调用。 我做错了什么? 问题答案: 枚举中的每个常量都是一个静态的最终嵌套类。因此,要模拟它,您必须在PrepareForTest中指向嵌套类。 返回预初始化的数组,因此在您的情况下也应进行模拟。 每个枚举常量都只

  • 第二个类有一个简单的方法,它只返回一个字符串,该字符串作为参数传递给该方法 现在有一个Sample类,我在Sample Classe的方法中调用了这个show方法并返回字符串。 现在我正在测试示例类,因此我创建了一个名为SampleTest的文件

  • 问题内容: 我知道关于模拟和测试有很多问题,但是我发现没有任何问题可以完美地帮助我,因此我仍然对理解以下内容有疑问: 如果我弄错了,请纠正我,但据我所知,单元测试用于隔离测试一个特定类的业务逻辑,并且如果有外部需要的任何对象,它们将被模拟。因此,例如,如果我有一个简单城市居民的管理系统,该系统将居民添加到列表中并按姓名返回居民(假设:居民仅包含一些基本个人信息),如下所示: 如果现在我要进行单元测

  • 问题内容: 我正在尝试第一次使用PowerMockito模拟类构造函数,但是它不起作用。我当前的代码是: 测试失败,因为返回的值为“ Fail”。我的问题在哪里? 问题答案: Okey,找到答案了,您需要致电 代替

  • 其中authUser()定义为final,如下所示: 我正在学习如何使用PowerMock模拟非静态方法,以及Powermockito是否可以模拟非final具体类中的final方法?。我尝试了一些变体,例如使用Mockito而不是PowerMock来存根authUser,以及将apiclientconnection.class添加到PrepareForTest注释中。我不明白为什么它不起作用。我