Search code examples
javaunit-testingjunitmockito

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


I have a method which puts InputStream in s3 bucket.

@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);
    }

I want to make the method cause IOException and then throw RuntimeException and I have written the unit test for the same.

@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));
    }

This is what I have tried but this is showing error in editor

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

How can I make the method throw IOException?


Solution

  • stream.mark(...) will never throw any checked exceptions anyway so this is rather pointless.

    You also don't need to mark or reset the stream manually at all. The SDK does that 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.

    If your stream is larger than 128 KB, then you may need to call setReadLimit to 1 byte greater than the size of the stream to avoid a ResetException when not using a FileInputStream. If it isn't, then you don't need to do anything. Regardless, you don't need to mark or reset the stream yourself.

    However, for reference purposes, Mockito.when(...).thenThrow(...) is for method invocations that return a value.

    stream.mark(mockStream.available()) is a method invocation that does not return a value (void return type).

    As per docs:

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

    In this case, replace:

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

    With:

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