Issue
I have a following piece of code which uploads json to s3
private final S3Client s3Client;
private final String bucketName;
public S3ClientWrapper(final Config s3Config) {
final String region = s3Config.getString(REGION_CONFIG_NAME);
bucketName = s3Config.getString(BUCKET_NAME);
final S3ClientBuilder s3ClientBuilder = S3Client.builder();
s3ClientBuilder.region(Region.of(region));
s3Client = s3ClientBuilder.build();
}
public boolean sendEvents(List<Obj> events) {
String key = getKey();
PutObjectRequest objectRequest = PutObjectRequest.builder()
.bucket(bucketName)
.key(key)
.build();
try {
s3Client.putObject(objectRequest, RequestBody.fromContentProvider(() -> {
PipedOutputStream outputStream = new PipedOutputStream();
GZIPOutputStream gzipOutputStream = null;
try {
gzipOutputStream = new GZIPOutputStream(outputStream);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
GZIPOutputStream finalGzipOutputStream = gzipOutputStream;
events.stream().map(Json::writeValueAsString)
.map(json -> json + "\n")
.forEach(json -> {
try {
finalGzipOutputStream.write(json.getBytes(StandardCharsets.UTF_8));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
try {
return new PipedInputStream(outputStream);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}, "application/octet-stream"));
return true;
}catch (S3Exception e) {
log.error("failed to send raw data events to S3", e);
return false;
}
}
I want to unit test cases where it fails to upload to S3 and raises an S3Exception. How can I write a unit test to test such a scenario in my function ?
Solution
I use Mockito here, but the mechanism would be similar in case of other libraries. We need to create the mocked s3Client
and inject it into the tested class. Before calling the tested method, we have to declare the expected behaviour of the mock, which in this case would be throwing S3Exception
.
Since a static S3Client.builder()
method is used, we need to use the mockStatic
method to mock the client creation and we'll need mockito-inline
to do that (I will link the docs references below).
@Test
void s3ExceptionThrown() {
try (var mocked = Mockito.mockStatic(S3Client.class)) {
// creation of the two mock objects below can be moved before the try
var builder = mock(S3ClientBuilder.class);
var s3Client = mock(S3Client.class);
// to mock a static method we have to use `when` method from the object created by `mockStatic`
mocked.when(S3Client::builder).thenReturn(builder);
// here we mock non-static methods, so standard `Mockito.mock` method can be used
when(builder.build()).thenReturn(s3Client);
when(s3Client.putObject(any(), any(), any()).thenThrow(new S3Exception());
var testedWrapper = new S3ClientWrapper(/* config */);
var result = testedWrapper.sendEvents(null); // param irrelevant for this test
assertFalse(result);
}
}
(important note: the code was written in a notepad on a mobile phone, so it may not compile well, but the mechanism is shown and as a concept should be working fine)
Answered By - Jonasz
Answer Checked By - Katrina (JavaFixing Volunteer)