- Newest
- Most votes
- Most comments
Please note that in AWS SDK 2.x, the fluent builders approach is indeed used extensively for constructing objects. While some of the objects might not provide explicit getter methods, you can still test their properties using various strategies. Please see how you can handle the situation:
-
Reflection: Although using reflection is not always recommended due to encapsulation and maintainability concerns, you can use it to access private fields and verify their values. You can retrieve the private fields of an object and assert their values in your unit tests. Keep in mind that this approach might break if the internal structure of the SDK classes changes.
-
Integration Testing: Instead of solely relying on unit tests, consider using integration tests with real AWS services. You can create actual AWS resources (with proper cleanup afterward) and test your SDK calls against them. This can help you ensure that your code interacts correctly with AWS services.
-
Wrapper Classes: Consider creating your own wrapper classes around the AWS SDK objects. These wrapper classes can expose getter methods and be designed to improve testability. While this adds a layer of abstraction, it can provide a cleaner and more maintainable approach for testing.
Please see an example of using reflection to test the properties of an AWS SDK 2.x object:
import software.amazon.awssdk.http.apache.ApacheHttpClient; import software.amazon.awssdk.http.apache.ProxyConfiguration; import org.junit.jupiter.api.Test; import java.lang.reflect.Field; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.is; public class ApacheHttpClientTest { @Test public void testApacheHttpClientProperties() throws Exception { ProxyConfiguration proxyConfig = ProxyConfiguration.builder().build(); ApacheHttpClient client = ApacheHttpClient.builder() .proxyConfiguration(proxyConfig) .connectionTimeout(50000) .socketTimeout(150000) .build(); // Use reflection to access private fields Field connectionTimeoutField = client.getClass().getDeclaredField("connectionTimeout"); Field socketTimeoutField = client.getClass().getDeclaredField("socketTimeout"); connectionTimeoutField.setAccessible(true); socketTimeoutField.setAccessible(true); assertThat(connectionTimeoutField.get(client), is(50000)); assertThat(socketTimeoutField.get(client), is(150000)); } }
Again, remember that using reflection in this manner has potential downsides, so consider other options like integration testing or creating wrapper classes if they better suit your needs.
Other few approaches you can take to test objects from the AWS SDK 2.x that don't have getters:
-
Expose the fields as public and access them directly in your tests. This breaks encapsulation but allows you to verify field values.
-
Add "builder gets" methods to return the configured values without building the object.
public long getConnectionTimeout() {
return connectionTimeout;
}
-
Build the object then call getters added specifically for testing. Mark them as @VisibleForTesting.
-
Build spies of the objects and use when/thenVerify to check calls to the builder.
ApacheHttpClient spy = spy(ApacheHttpClient.builder().build());
verify(spy).connectionTimeout(50000);
-
Serialize/deserialize the object and compare the serialized form for equality.
-
Wrap the SDK object in your own class with getters and test that class.
In summary - either expose fields, add test-only getters, use spies/whenVerify, or serialize/compare to verify configured values. Breaking encapsulation a bit may be necessary given the SDK changes.
Last approach(similar to the first explanation):
In AWS SDK 2.x, the objects are built using static builders instead of constructors and setter functions, which means you cannot directly access the fields of the object using getter functions. However, you can still write unit tests for these objects by using the ApacheHttpClient.builder()
method to construct the object and then verifying the desired properties using the assertThat()
method in JUnit.
Pleas see an example of how you can test the ApacheHttpClient
object:
public void testApacheHttpClientProperties() {
ApacheHttpClient client = ApacheHttpClient.builder()
.proxyConfiguration(new ProxyConfiguration())
.connectionTimeout(50000)
.socketTimeout(150000)
.build();
assertThat(client.getProxyConfiguration(), is(notNullValue());
assertThat(client.getConnectionTimeout(), is(5000);
assertThat(client.getSocketTimeout(), is(15000);
}
In this example, we use the ApacheHttpClient.builder()
method to construct the object and then verify that the proxyConfiguration
field is not null and that the connectionTimeout
and socketTimeout
fields are set to the expected values.
To verify the proxyConfiguration
field, you can use the is(notNullValue()
method, which checks if the field is not null.
To verify the connectionTimeout
and socketTimeout
fields, you can use the is(equalTo()
method, which compares the value of the field to the expected value.
You can also use the hasField()
method to check if the field is present in the object and has the expected value.
assertThat(client.hasField("connectionTimeout", is(equalTo(5000));
assertThat(client.hasField("socketTimeout", is(equalTo(15000));
Please see relevant documentation links to look at:
Exposing fields directly
- This breaks encapsulation but allows direct access for testing:
Adding builder get methods
- Example of adding "get" methods specifically for testing purposes:
- https://aws.github.io/aws-sdk-java-v2/javadoc/com/amazonaws/auth/ ↗ profile/ProfileCredentialsProvider.Builder.html
Using @VisibleForTesting
- Annotation to mark methods only visible for testing:
Using test doubles (spies)
- Verify builder calls using spies and when/thenVerify:
Serialization for equality
- Compare serialized objects for deep equality:
Wrapping in testable class
- Encapsulate SDK objects in your own testable classes:
AWS SDK for Java Documentation: Visit the official AWS SDK for Java documentation to find information about classes, methods, and usage of the SDK: https://docs.aws.amazon.com/sdk-for-java/latest/index.html
API Documentation: Look for the specific service documentation you're working with. For example, if you're dealing with the S3 service, the documentation can be found here: https://docs.aws.amazon.com/sdk-for-java/latest/developer-guide/examples-s3.html
JavaDocs: The JavaDocs provide detailed information about the classes, methods, and fields in the SDK. You can generate JavaDocs for the SDK and access them to understand the structure of the classes and their fields: https://sdk.amazonaws.com/java/api/latest/
For reflection in Java:
Java Reflection Tutorial: To understand how reflection works in Java and how to access private fields and methods, you can refer to the official Java tutorial: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/reflect/package-summary.html
- Reflection API: Explore the Java Reflection API documentation to understand the classes and methods available for reflective operations: https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/reflect/Package-summary.html
Thank you for your detailed response. That is helpful.
I had really hoped to avoid breaking encapsulation, but I believe that will be required. Integration tests will not work as it would be very difficult to integration-test properties like socketTimeout. Adding abstractions introduces more code that itself would need to be tested, leaving us back where we started. A unit test is simply the best way I know to test things like verifying the configuration properties have been correctly set in the aws objects.
I understand the benefits of the fluent builder pattern and removing setter functions, but I’m curious - what is the benefit to excluding public getter functions such that breaking encapsulation is required to learn the state of the object and write unit tests? Is there any chance AWS will add getter functions in the future for properties set during object construction?
Also, thank you for the links. One note: one of the links was broken for me (returned 404): https://aws.github.io/aws-sdk-java-v2/javadoc/com/amazonaws/auth/
Relevant content
- asked 2 years ago
- asked 3 years ago
- asked 2 years ago
- AWS OFFICIALUpdated 3 months ago
- AWS OFFICIALUpdated a year ago
- AWS OFFICIALUpdated 3 years ago
- AWS OFFICIALUpdated 5 months ago