【问题标题】:Mock openInputStream read method模拟 openInputStream 读取方法
【发布时间】:2019-02-16 06:35:56
【问题描述】:

我正在从 openInputStream 读取数据块并想模拟它的读取方法。

 final byte[] testFileData = { 0x74, 0x65, 0x73, 0x74, 0x20, 0x64, 0x61, 0x74, 0x61, 0x0d, 0x0a};

 CloudBlob cloudBlob = this.getCloudBlobContainer().getBlobReferenceFromServer(blobName);

    ByteArrayOutputStream blobStream = new ByteArrayOutputStream();

    try (final InputStream inputStream =  cloudBlob.openInputStream()) {

        //Read 4MB chunks of data 
        byte[] bufferToRead = new byte[4 * 1024 *1024];
        int bytesRead = inputStream.read(bufferToRead );

        while (bytesRead > 0) {
            //Add only the total number of bytes read to Bytearrayoutputstream
            blobStream.write(bufferToRead, 0, bytesRead);
            bytesRead = inputStream.read(bufferToRead);                
        }
    } `

我模拟了 InputStream,但在模拟它的 read 方法时发现困难,因为它接受缓冲区作为引用并在读取后将字节数组复制到它。

 @Mock
private BlobInputStream inputStream;


       // Mock
        when(cloudBlobContainer.getBlobReferenceFromServer(anyString())).thenReturn(cloudBlob);
        when(cloudBlob.openInputStream()).thenReturn(inputStream);  

【问题讨论】:

    标签: java junit mockito azure-storage powermockito


    【解决方案1】:

    InputStream 很难模拟,尤其是因为它的三个read 覆盖以及在以后的Java 版本中添加的readNBytesreadAllBytes 方法。如果使用 Mockito 完成,您将需要让所有这些方法实现与相同的数据交互,否则您将得到一个脆弱的测试,一旦您的实现调用不同的 InputStream 方法,它可能会中断。如果你必须使用一个测试替身,你最好写一个“假”,但是当Java内置ByteArrayInputStream时没有理由这样做:你可以构造你的byte[]缓冲区(或编写一个辅助方法根据您的测试需要构建一个)并将其替换为您的测试中的 InputStream。 这对于遇到这个问题的人来说应该足够了,只是询问关于模拟 InputStream 的一般情况。

    final byte[] testFileData = {
        0x74, 0x65, 0x73, 0x74, 0x20, 0x64, 0x61, 0x74, 0x61, 0x0d, 0x0a};
    ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(testFileData);
    

    不幸的是,这并没有回答有关如何专门为 Azure 的BlobInputStream 提供测试夹具的问题,因为BlobInputStream has a four-arg constructor with a lot of Azure internals 这特别棘手。好消息是,从 1.9.5 开始,Mockito 提供了一个 delegatesTo method in AdditionalAnswers 记录(我自己强调)为:

    直接将呼叫转发给代理的应答。 委托可能与模拟的类型相同,也可能不同。如果类型不同,则需要在委托类型上找到匹配的方法,否则会引发异常。

    这意味着您可以通过创建一个真正的 ByteArrayInputStream 并将可覆盖的方法委托给它来模拟 BlobInputStream。不幸的是,delegatesTo 位于 AdditionalAnswers 而不是 Answers 枚举(无论如何它需要一个您无法在注释中提供的实例参数),因此您需要手动构建您的模拟:

    BlobInputStream mockInputStream = Mockito.mock(
        BlobInputStream.class,
        AdditionalAnswers.delegatesTo(byteArrayInputStream));
    
    when(cloudBlobContainer.getBlobReferenceFromServer(anyString()))
        .thenReturn(cloudBlob);
    when(cloudBlob.openInputStream()).thenReturn(mockInputStream);
    

    但是,如果可能的话,这将是一个很好的机会,可以将您的代码分为 处理 Azure 输入流的部分您的应用程序代码处理任何 InputStream 的部分 .例如,如果您的代码采用 BlobInputStream 并运行解析或纠错代码,您可以分解出一个方法 handleInputStream(InputStream) 并传入您自己的 ByteArrayInputStream 以对其进行大量测试。这可以最大限度地减少您模拟 BlobInputStream 的需要,或者如果您选择仅使用真实后端将 BlobInputStream 处理作为 集成测试 进行测试,则可以完全消除它。另请参阅Mocking Files in Java - Mock Contents - Mockito,其中 Brice 讨论了单元测试和集成测试之间的类似拆分,而不是模拟 java.io.File 实例。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多