I have a method to upload directory to s3. I would like to write a unit test for it and I've been searching, but most test are integration tests. Maybe I'm not supposed to do a unit test for this kind of process? I don't have a privileges to use testcontainers for integration test, so I can’t use testcontainers.
Here is my code
public void uploadDir(final Path path, final String key) throws InternalServerError {
final TransferManager transferManager = TransferManagerBuilder.standard()
.withS3Client(s3Client)
.build();
final ProgressListener progressListener = progressEvent -> {
if (progressEvent.getBytesTransferred() > 0) {
double percentTransferred = progressEvent.getBytesTransferred() * 100.0 / progressEvent.getBytes();
log.info("Transferred " + percentTransferred + "%");
}
};
final MultipleFileUpload xfer = transferManager.uploadDirectory("mybucket", key, path.toFile(), true);
xfer.addProgressListener(progressListener);
xfer.waitForCompletion();
log.info("Upload has been completed.");
} catch (final AmazonServiceException | InterruptedException exception) {
... do something here
} finally {
transferManager.shutdownNow();
}
}
It seems there's not much to unit test in the method but arguments and interactions.
Mocking TransferManager
isn't really a solution because you'd need to mock MultipleFileUpload
, and also the code that calls ProgressListener
needs to be added. You'd end up mocking everything which makes it useless. Testing a widely used library also has not much value for you.
I'd test the exception case which is an unlikely case to happen (aka not repeatable) during integration testing.
Roughly something like this (assuming mockito):
@Test
void uploadDirFailsTest() {
TransferManager mockTM = mock(TransferManager.class);
doThrow(new AmazonServiceException(...)) // hopefully it has a public constructor
.when(mockTM)
.uploadDirectory(eq("mybucket"), any(), any(), eq(true)); //more matching if needed
YourClassName instance = new YourClassName(mockTM);
instance.uploadDir(path, key);
/*
verify correct exception handling here or
if you rethrow the exception wrap the call with
assertThrows()
*/
}
public class YourClassName {
final TransferManager transferManager;
public YourClassName(TransferManager transferManager) {
this.transferManager = transferManager;
}
public void uploadDir(final Path path, final String key) throws InternalServerError {
final ProgressListener progressListener = progressEvent -> {
if (progressEvent.getBytesTransferred() > 0) {
double percentTransferred = progressEvent.getBytesTransferred() * 100.0 / progressEvent.getBytes();
log.info("Transferred " + percentTransferred + "%");
}
};
final MultipleFileUpload xfer = transferManager.uploadDirectory("mybucket", key, path.toFile(), true);
xfer.addProgressListener(progressListener);
xfer.waitForCompletion();
log.info("Upload has been completed.");
} catch (final AmazonServiceException | InterruptedException exception) {
... do something here
} finally {
transferManager.shutdownNow();
}
}
//real use
new YourClassName(
TransferManagerBuilder.standard()
.withS3Client(s3Client)
.build()
);