Reputation: 33
I have one method that uploads files to Amazon S3. I am trying to write JUnit for this method but get NullPointerException on the S3AsyncClient:
my class:
public class S3Client<T> {
private static final Logger log = LoggerFactory.getLogger(S3Client.class);
S3AsyncClient client;
/**
*
* @param s3Configuration
*/
public S3Client(AWSS3Configuration s3Configuration) {
this.client = s3Configuration.getAsyncClient();
}
/**
* Uploads a file s3 bucket and returns etag
* @param uploadData
* @return
* @throws S3Exception
*/
public CompletableFuture<String> uploadFile(S3UploadData<T> uploadData) throws S3Exception {
int contentLength;
AsyncRequestBody asyncRequestBody;
if(uploadData.getContent() instanceof String) {
String content = (String) uploadData.getContent();
contentLength = content.length();
asyncRequestBody = AsyncRequestBody.fromString(content);
}
else if(uploadData.getContent() instanceof byte[]){
byte[] bytes = (byte[]) uploadData.getContent();
contentLength = bytes.length;
asyncRequestBody = AsyncRequestBody.fromBytes(bytes);
}
else{
throw new IllegalArgumentException("Unsupported upload content type");
}
PutObjectRequest putObjRequest = PutObjectRequest.builder()
.bucket(uploadData.getBucketName())
.key(uploadData.getFileName())
.metadata(uploadData.getMetaData())
.contentLength((long) contentLength).build();
CompletableFuture<String> response = client.putObject(putObjRequest, asyncRequestBody).thenApply(
getPutObjectResponse -> {
log.info("Got response from S3 upload={}", getPutObjectResponse.eTag());
return getPutObjectResponse.eTag();
});
response.exceptionally(throwable -> {
log.error("Exception occurred while uploading a file intuit_tid={} file={}",uploadData.getTransactionId(),uploadData.getFileName());
throw new S3Exception(throwable.getMessage());
});
return response;
}
input for this class object of S3UploadData:
`
@Getter
@AllArgsConstructor
public class InputData<T> {
T content;
String fileName;
String bucketName;
String transactionId;
Map<String, String> metaData;
}`
can u help please with writing Junit for uploadFile method?
Upvotes: 2
Views: 2493
Reputation: 4749
You could use Mockito to mock the S3AsyncClient operations.
@Mock
private S3AsyncClient s3AsyncClient;
Below is the unit test case for my upload file implementation. It will surely give you insights how it is done and it can be done.
@Nested
class UploadFile {
@Captor
ArgumentCaptor<PutObjectRequest> putObjectRequestCaptor;
@Captor
ArgumentCaptor<AsyncRequestBody> requestBodyCaptor;
@Test
void testSuccessfulUpload() {
Flux<ByteBuffer> body = Flux.just();
var expectedResponse = PutObjectResponse.builder().build();
when(s3AsyncClient.putObject(putObjectRequestCaptor.capture(), requestBodyCaptor.capture())) .thenReturn(CompletableFuture.completedFuture(expectedResponse));
fileUploadService.upload("TEST_PREFIX", "test.zip", body);
assertThat(putObjectRequestCaptor.getValue().bucket()).isEqualTo(TEST_BUCKET);
assertThat(putObjectRequestCaptor.getValue().key()).isEqualTo("TEST_PREFIX/test.zip");
assertThat(requestBodyCaptor.getValue()).isNotNull();
}
}
Upvotes: 0
Reputation: 10704
You have no JUNIT test code. You should have code that uses org.junit.jupiter.api.*
Instead of using a MOCK, call the actual S3 Async code in a @TestInstance integration test to make sure it works. For example, here is my test in IntelliJ.
As you can see, my test passed and I Know my code works -- which is the point of this AWS integration test.
If my code failed or threw an exception for some reason, my test would fail. For example, if I passed a bucket name that does not exist, I would get:
Here is my Java Amazon S3 Async code:
package com.example.s3.async;
import software.amazon.awssdk.core.async.AsyncRequestBody;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3AsyncClient;
import software.amazon.awssdk.services.s3.model.PutObjectRequest;
import software.amazon.awssdk.services.s3.model.PutObjectResponse;
import java.nio.file.Paths;
import java.util.concurrent.CompletableFuture;
// snippet-end:[s3.java2.async_ops.import]
// snippet-start:[s3.java2.async_ops.main]
/**
* To run this AWS code example, ensure that you have setup your development environment, including your AWS credentials.
*
* For information, see this documentation topic:
*
* https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/get-started.html
*/
public class S3AsyncOps {
public static void main(String[] args) {
final String USAGE = "\n" +
"Usage:\n" +
" S3AsyncOps <bucketName> <key> <path>\n\n" +
"Where:\n" +
" bucketName - the name of the Amazon S3 bucket (for example, bucket1). \n\n" +
" key - the name of the object (for example, book.pdf). \n" +
" path - the local path to the file (for example, C:/AWS/book.pdf). \n";
if (args.length != 3) {
System.out.println(USAGE);
System.exit(1);
}
String bucketName = args[0];
String key = args[1];
String path = args[2];
Region region = Region.US_WEST_2;
S3AsyncClient client = S3AsyncClient.builder()
.region(region)
.build();
putObjectAsync(client, bucketName, key, path);
}
public static void putObjectAsync(S3AsyncClient client,String bucketName, String key, String path) {
PutObjectRequest objectRequest = PutObjectRequest.builder()
.bucket(bucketName)
.key(key)
.build();
// Put the object into the bucket
CompletableFuture<PutObjectResponse> future = client.putObject(objectRequest,
AsyncRequestBody.fromFile(Paths.get(path))
);
future.whenComplete((resp, err) -> {
try {
if (resp != null) {
System.out.println("Object uploaded. Details: " + resp);
} else {
// Handle error
err.printStackTrace();
}
} finally {
// Only close the client when you are completely done with it
client.close();
}
});
future.join();
}
}
Now for my test, i want to call this code, not MOCK it. I have setup my test in IntelliJ like this,
import org.junit.jupiter.api.*;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import software.amazon.awssdk.regions.Region;
import java.io.*;
import java.util.*;
import com.example.s3.async.*;
import software.amazon.awssdk.services.s3.S3AsyncClient;
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class AmazonS3AsyncTest {
private static S3AsyncClient s3AsyncClient;
// Define the data members required for the tests
private static String bucketName = "";
private static String objectKey = "";
private static String objectPath = "";
private static String toBucket = "";
@BeforeAll
public static void setUp() throws IOException {
// Run tests on Real AWS Resources
s3AsyncClient = S3AsyncClient.builder()
.region(Region.US_EAST_1)
.build();
try (InputStream input = AmazonS3Test.class.getClassLoader().getResourceAsStream("config.properties")) {
Properties prop = new Properties();
if (input == null) {
System.out.println("Sorry, unable to find config.properties");
return;
}
//load a properties file from class path, inside static method
prop.load(input);
// Populate the data members required for all tests
bucketName = prop.getProperty("bucketName");
objectKey = prop.getProperty("objectKey");
objectPath= prop.getProperty("objectPath");
toBucket = prop.getProperty("toBucket");
} catch (IOException ex) {
ex.printStackTrace();
}
}
@Test
@Order(1)
public void whenInitializingAWSS3Service_thenNotNull() {
assertNotNull(s3AsyncClient);
System.out.println("Test 1 passed");
}
@Test
@Order(2)
public void putObject() {
S3AsyncOps.putObjectAsync(s3AsyncClient, bucketName, objectKey, objectPath);
System.out.println("Test 2 passed");
}
}
Upvotes: 1