Skip to content

Increase code coverage on dynamodb-enhanced module#6700

Open
andreas-grafenberger wants to merge 7 commits intoaws:masterfrom
anasatirbasa:437-increase_test_coverage
Open

Increase code coverage on dynamodb-enhanced module#6700
andreas-grafenberger wants to merge 7 commits intoaws:masterfrom
anasatirbasa:437-increase_test_coverage

Conversation

@andreas-grafenberger
Copy link
Contributor

@andreas-grafenberger andreas-grafenberger commented Feb 2, 2026

Motivation and Context

The goal of this work is to increase unit, functional, and integration code coverage for the
aws-sdk-java-v2/services-custom/dynamodb-enhanced module.

Initial coverage:

  • Class: 97%
  • Method: 85%
  • Line: 87%
  • Branch: 64%

Target coverage:

  • Class: ≥ 98%
  • Method: ≥ 92%
  • Line: ≥ 92%
  • Branch: ≥ 85%

Achieved coverage:

  • Class: 99%
  • Method: 92%
  • Line: 94%
  • Branch: 91%

These results exceed the defined targets and significantly improve confidence in the correctness and stability of the enhanced DynamoDB module.


Modifications

This pull request expands code coverage across the DynamoDB Enhanced Client by introducing new unit, functional, and integration tests targeting previously uncovered or partially covered code paths.


Testing

  • Existing test suites were executed without regression.
  • New tests were added to specifically target uncovered branches and methods.
  • All new and existing tests pass successfully.
  • Coverage was measured locally after test execution to validate improvements.

Screenshots

Initial coverage:

codeCoverage-before

Achieved coverage:

codeCoverage-after

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)

Checklist

  • I have read the CONTRIBUTING document
  • Local run of mvn install succeeds
  • My code follows the code style of this project
  • My change requires a change to the Javadoc documentation
  • I have updated the Javadoc documentation accordingly
  • I have added tests to cover my changes
  • All new and existing tests passed
  • I have added a changelog entry. Adding a new entry must be accomplished by running the scripts/new-change script and following the instructions. Commit the new file created by the script in .changes/next-release with your changes.
  • My change is to implement 1.11 parity feature and I have updated LaunchChangelog

License

  • I confirm that this pull request can be released under the Apache 2 license

@andreas-grafenberger andreas-grafenberger changed the title 437 Increase test coverage Increase test coverage Feb 2, 2026
@anasatirbasa anasatirbasa force-pushed the 437-increase_test_coverage branch from a024740 to fcfbd00 Compare February 10, 2026 17:09
@andreas-grafenberger andreas-grafenberger changed the title Increase test coverage Increase code coverage Feb 10, 2026
@andreas-grafenberger andreas-grafenberger changed the title Increase code coverage Increase code coverage on dynamodb-enhanced module Feb 10, 2026
@anasatirbasa anasatirbasa force-pushed the 437-increase_test_coverage branch from 337c117 to f97adfc Compare February 11, 2026 14:26
@andreas-grafenberger andreas-grafenberger marked this pull request as ready for review February 16, 2026 15:56
@andreas-grafenberger andreas-grafenberger requested a review from a team as a code owner February 16, 2026 15:57
@Test
public void returnsCorrectOperationName() {
DeleteTableOperation<FakeItemWithIndices> operation = DeleteTableOperation.create();
assertThat(operation.operationName().label(), is("DeleteItem"));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is("DeleteItem") , should this be delete table ?

Copy link
Contributor Author

@andreas-grafenberger andreas-grafenberger Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, but this reflects the original implementation logic. However, the scope of this PR is purely to improve test coverage. I suggest to avoid mixing different types of changes here - the fix for this operation name should be treated separately. What do you think?

L.E: updated assert by replacing literal string "DeleteItem" with the label value from enum.

if (o == null || getClass() != o.getClass()) {
return false;
}
StaticCounterRecord that = (StaticCounterRecord) o;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be ?

BeanCounterRecord that = (BeanCounterRecord) o;

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated with the correct type; also, regenerated equals and hashCode methods for both StaticCounterRecord and BeanCounterRecord.


AutoGeneratedTimestampRecordExtension.Builder rebuilt = extension.toBuilder();

assertThat(rebuilt).isNotNull();

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we only check is it is not null but shouldn't we check if we preserved the value ?\

assertThat(generatedInstant).isEqualTo(Instant.parse("2025-01-01T00:00:00Z"));

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated the assert to compare both objects (incl. private fields - like clock): initial extension vs rebuilt extension.


@TestFactory
Stream<DynamicTest> testDefaultMethodsThrowUnsupportedOperation() {
return scanPackageForClasses(BASE_PACKAGE)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will silently return nothing and the test will be passed automatically. You may consider to check if we have any tests returned from the operation before returning values..

Stream<DynamicTest> testDefaultMethodsThrowUnsupportedOperation() {
    List<DynamicTest> tests = scanPackageForClasses(BASE_PACKAGE)
        .filter(Class::isInterface)
        .filter(this::hasDefaultMethods)
        .collect(toList())
        .stream()
        .flatMap(this::createTestsForInterface)
        .collect(toList());

    assertThat(tests).as("Expected to find interfaces with default UnsupportedOperation methods, "
                         + "but classpath scan returned nothing. Check classpath configuration.")
                     .isNotEmpty();

    return tests.stream();

Copy link
Contributor Author

@andreas-grafenberger andreas-grafenberger Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see your point - you are right - but I would suggest something more restrictive for that list (tests) - I added an assert to compare against the current number of methods that qualify for this test - current number: 100, and the assert looks something like:
assertEquals(100, tests.size());

I would take this approach because it works as an additional check when / if someone introduces such methods.

mappedTable.putItem(record);
RecordWithAutogeneratedUuid result = mappedTable.scan().items().stream().findFirst()
.orElseThrow(() -> new AssertionError("No record found"));
isValidUuid(result.getId());

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not an assertion but I think we will be fixed in PR 6373 and we can rebaseline on that

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, tests containing isValidUuid(*) (and no other assertion) will be fixed in mentioned PR and then we can double check to be sure that we don't miss anything on this PR.

return sort;
}

public String stringAttribute() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be getStringAttribute() ?

I think the SDK's ImmutableTableSchema uses getter naming conventions. This may cause the attribute to not be mapped correctly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there are some checks regarding get / set methods and an exception is thrown if there are problems, but these tests are not affected by this scenario. Regardless, I appreciate the suggestion and have implemented the fix to ensure the code remains consistent.

return sort;
}

public String stringAttribute() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same as in abstract model, shouldn't this be getStringAttribute ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above: yes, there are some checks regarding get / set methods and an exception is thrown if there are problems, but these tests are not affected by this scenario. Regardless, I appreciate the suggestion and have implemented the fix to ensure the code remains consistent.

}

@Test
public void createKeyFromItem_withPartitionKeyOnly_createsCorrectKey() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this really different than createKeyFromItem_partitionKeyOnly_returnsKeyWithPartitionOnly ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed duplicated tests.

}

@Test
public void keyRef_withSpecialCharacters_cleansAndFormatsKey() {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this testing something different than keyRef_attributeNameWithSpecialCharacters_returnsCleanedReference ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as above, I removed the duplicated tests.

import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbBean;
import software.amazon.awssdk.enhanced.dynamodb.mapper.annotations.DynamoDbPartitionKey;

public class ChainExtensionTest extends LocalDynamoDbSyncTestBase {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think more important part of chaining extensions is interaction across operations. Missing: updateItem (does version increment while UUID stays stable?)

Could you add tests for updateItem, batchWriteItem, and transactWriteItems?

The updateItem case will be worth to test, AutoGeneratedUuidExtension always regenerates the UUID on every write, while VersionedRecordExtension increments the version. What happens when both run on an update? Does the version increment correctly? Does the timestamp update?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good one - thanks! I will give it a try...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added tests in ChainExtensionTest class:

  • transactWriteItem_putItemThenUpdateItemAndGetItem_appliesAllChainedExtensions
  • putItemThenUpdateItemAndGetItem_appliesAllChainedExtensions
  • batchWriteItem_putTwoItems_appliesAllChainedExtensionsWithoutVersion
  • batchWriteItem_putTwoItemsAndAppliesAllChainedExtensions_throwsIllegalArgumentException

EnumAttributeConverter<Person> personConverter = EnumAttributeConverter.createWithNameAsKeys(Person.class);

Person john = personConverter.transformTo(AttributeValue.fromS("JOHN"));
personConverter.transformTo(AttributeValue.fromS("JOHN"));

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should also check the correct enum value is returned, something like

assertThat(personConverter.transformTo(AttributeValue.fromS("JOHN"))).isEqualTo(Person.JOHN);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good one, thank you! The test should be fine now.

import software.amazon.awssdk.services.dynamodb.model.ReturnValue;
import software.amazon.awssdk.services.dynamodb.model.TableDescription;

public class AnnotatedBeanTableSchemaTest extends LocalDynamoDbSyncTestBase {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use a parameterized test and remove the Immutable and Async variants? They are largely the same code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can do it for Bean and Immutable, but not for Sync and Async since there is used a different client type of DynamoDb (sync vs async and is initialized based on inherited test class):

  • LocalDynamoDbSyncTestBase is used to create DynamoDbClient
  • LocalDynamoDbAsyncTestBase is used to create DynamoDbAsyncClient

Also, there is an existing "pattern" where tests are already separated based on the used DynamoDb client type (tests for Async client usually starts with "Async*").

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Update - I parameterized the mentioned tests (in terms of Bean and Immutable variants) :

  • AnnotatedBeanTableSchemaTest.java
  • AnnotatedImmutableTableSchemaTest.java

as: AnnotatedTableSchemaTest.java, and:

  • AsyncAnnotatedBeanTableSchemaTest.java
  • AsyncAnnotatedImmutableTableSchemaTest.java

as: AsyncAnnotatedTableSchemaTest.java

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants