执行 S3 putObject 时 stream.mark(stream.available) 的 JUnit 和 Mockito 单元测试

JUnit and Mockito unit test for stream.mark(stream.available) when doing S3 putObject

我有一个将 InputStream 放入 s3 存储桶的方法。

@Override
    public String putObject(@NonNull String s3BucketName, @NonNull String s3Key, @NonNull String content,
                            @NonNull ObjectMetadata metadataRequest) {
        InputStream stream = new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8));
        try {
            stream.mark(stream.available());
        } catch (IOException e) {
            String errorMessage = String.format("Runtime error while marking stream.",
                    s3BucketName, s3Key);
            throw new RuntimeException(errorMessage, e);
        }
        PutObjectRequest request = new PutObjectRequest(s3BucketName, s3Key, stream, metadataRequest);
        return putObject(request);
    }

我想让方法引发 IOException,然后抛出 RuntimeException,我已经为此编写了单元测试。

@Test
    public void putObjectTest_withStringContent_withMetadataRequest_IOError() {
        ObjectMetadata metadataRequest = new ObjectMetadata();
        metadataRequest.addUserMetadata(TEST_METADATA_KEY, TEST_METADATA_VALUE);
        InputStream mockStream = Mockito.mock(InputStream.class);
        Mockito.when(mockStream.mark(mockStream.available())).thenThrow(IOException.class);

        assertThrows(RuntimeException.class, () -> s3Accessor.putObject
                (TEST_S3BUCKET, TEST_S3OBJECT, TEST_STRING, metadataRequest));
    }

这是我尝试过的方法,但在编辑器中显示错误

Required type:
T
Provided:
void
reason: no instance(s) of type variable(s) T exist so that void conforms to T

如何使方法抛出 IOException?

stream 是一个 ByteArrayInputStream。调用 mark 时永远不会抛出任何异常。文档明确指出不能抛出 IOException,并且忽略参数。

异常不是来自 mark 调用,而是来自 available 调用。由于参数被忽略,只需将其替换为 0 即可,不需要捕获 IOException。事实上,你会得到一个编译器错误:

exception java.io.IOException is never thrown in body of corresponding try statement

stream.mark(...) 无论如何都会 never throw any checked exceptions 所以这是毫无意义的。

您也根本不需要手动标记或重置流。 SDK 会这样做 behind the scenes.

By default, the AWS SDK for Java attempts to retry failed transfers by marking the input stream before the start of a transfer and then resetting it before retrying.

如果您的流大于 128 KB,那么您可能需要调用 setReadLimit 以比流的大小大 1 个字节,以避免在不使用 [=15] 时出现 ResetException =].如果不是,则您无需执行任何操作。无论如何,您不需要自己标记或重置流。

但是,出于参考目的,Mockito.when(...).thenThrow(...) 用于 return 一个值的方法调用。

stream.mark(mockStream.available()) 是一种方法调用,它 不是 return 一个值(void return 类型)。

根据docs

Use doThrow() when you want to stub the void method with an exception.

在这种情况下,替换:

Mockito.when(mockStream.mark(mockStream.available())).thenThrow(IOException.class);

与:

doThrow(IOException.class).when(mockStream).mark(mockStream.available());