我正在从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);
InputStream非常难以模拟,尤其是因为它有三个read
覆盖以及后来的Java版本中添加的readNBytes
和readAllBytes
方法。如果使用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实例。