Unit testing AWS SDK in TypeScript

Phillip Le
4 min readSep 16, 2023

--

Take a look at the source code in GitHub.

Unit testing AWS SDK v3 using aws-sdk-client-mock

I’ll use a simple CRUD application which creates a user account and persists it to DynamoDB using AWS SDK v3 as an example. DynamoDB has a few libraries like @aws-sdk/client-dynamodb and @aws-sdk/lib-dynamodb. @aws-sdk/client-dynamodb is generally used for table level commands such as CreateTableCommand and DescribeTableCommand. For CRUD operations, it helpful to use @aws-sdk/lib-dynamodb which automatically handles marshalling and unmarshalling javascript objects to DynamoDB JSON.

yarn add @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb

You can initialise the DynamoDB client in its own file and export it globally so you can use it throughout your application:

Source

We can create a user object and persist it to DynamoDB with the PutCommand:

Source

To test this we can use the aws-sdk-client-mock library which provides a simple way to mock AWS SDK v3 clients:

yarn add -D aws-sdk-client-mock

In our test file, we can then mock the DynamoDB Document Client that we created earlier.

Source

In our test case, we can setup the expected response from the PutCommand:

Source

For creating a user, we do not use the response from the PutCommand so we can just return an empty object.

On the other hand, if we wanted to get a list of users by their role:

Source

We would need to setup the list users that DynamoDB would return:

Source

We also want to assert that we are giving the DynamoDB client commands with the correct parameters, for example with the PutCommand we want to use the correct table name and the user we expect to create. We can use the custom jest matchers provided by aws-sdk-client-mock-jest to do this:

yarn add -D aws-sdk-client-mock-jest

Make sure to import aws-sdk-client-mock-jest at the top of your test file:

import 'aws-sdk-client-mock-jest';

Then, we can use the toHaveReceivedCommandWith matcher to assert that the client has received a PutCommand with the correct parameters:

Source

Similarly, we can use the toHaveReceivedCommandWith matcher to assert that the client has received a QueryCommand with the correct parameters:

Source

Comparing testing approach to AWS SDK v2

aws-sdk-client-mock makes it very easy to unit test AWS SDK v3. However, the testability of our implementation is highly reliant on aws-sdk-client-mock. If we look at how our unit tests would have been implemented in AWS SDK v2 where an equivalent library did not exist, we can see that our tests require more setup and understanding of jest mocking.

This is how we would query users by their role in AWS SDK v2:

Source

Notice that AWS SDK v2 does not support promises initially, so we need to call the promise method to get a promise. This makes mocking more complex:

Source

We can see above that while it is possible to unit test AWS SDK v2, it was not intuitive how to do so. Ideally, the testability of our application should not be heavily reliant on the implementation details of third party libraries.

Wrapping the AWS SDK client methods

One of the core challenges of testing the AWS SDK library is that the client methods are not easily mockable. jest tends to be simpler when mocking functions as opposed to classes. We can wrap the AWS SDK v3 client methods in functions which pass the parameters to the client method and return the results.

Source

One of the key considerations is that this wrapper function should not add additional logic, so that we are effectively testing at the same level as before.

Using our wrapper looks like this:

Source

The queryDynamoDb wrapper function is much easier to mock than the send method of the DynamoDBDocumentClient class.

In our test file, we first mock the file that exports the AWS SDK wrapper functions:

jest.mock('../../../aws-sdk-v3/dynamodb');

This replaces all the functions exported by the file with mock functions. Then, we can mock the queryDynamoDb function using jest.mocked:

Source

We can also assert that queryDynamoDb was called with the correct parameters easily with toHaveBeenCalledWith:

Source

This way our implementation is not reliant on third party libraries like aws-sdk-client-mock and we can use standard jest mocking.

If we used AWS SDK v2, queryDynamoDb would look like this:

Source

But our tests would look almost exactly the same aside from typing differences.

Which can both be simplified using the built-in Parameters utility type:

Using wrapper functions is a great way to abstract some of the implementation details of third party libraries away from our own application. Unfortunately, if we needed to switch to using a different technology for persisting our user objects then you will find that the wrapper functions do not help us because they are inherently tied to DynamoDB.

--

--