avatarKris Raven

Summary

The author discusses updates to the AWS SDK v3 Client Mock, focusing on the introduction of custom Jest matchers to simplify tests and the use of an additional library called aws-sdk-client-mock-jest.

Abstract

The article begins with a brief introduction to the author's previous work on creating Jest unit tests for the AWS SDK v3 Client. The author then highlights the need for an additional library, aws-sdk-client-mock-jest, to make the tests cleaner and easier to understand. The library introduces custom Jest matchers, such as .toHaveReceivedCommandWith, which simplifies the test code. However, the author also mentions a couple of minor annoyances, such as the need to include a command in the assertion and the cryptic error messages. Despite these issues, the author concludes that the library is nice to work with and supports all AWS services they use. The article ends with a discussion on the potential overuse of the library's functionality and the importance of keeping tests easy to understand.

Bullet points

  • The author previously published an article on creating Jest unit tests for the AWS SDK v3 Client.
  • An additional library, aws-sdk-client-mock-jest, is needed to make the tests prettier and more Jest-like.
  • The library introduces custom Jest matchers, such as .toHaveReceivedCommandWith, which simplifies the test code.
  • One minor annoyance is the need to include a command in the assertion.
  • The error messages can be cryptic and unhelpful in some cases.
  • Despite these issues, the library is nice to work with and supports all AWS services the author uses.
  • The article warns against overusing the library's functionality and the importance of keeping tests easy to understand.
“Updates to the AWS SDK v3 Client Mock to make my Jest testing easier you say? Sign us up!” Image credit at the end the article.

Updates to the AWS SDK v3 Client Mock

Over a year ago, I published this article about creating Jest unit tests for the AWS SDK v3 Client. Since writing the article, there have been some further developments which I’ll cover in this article. I thought it’s worth revisiting as I have been updating some old AWS SDK v2 code to use v3, and updating the Jest Unit Tests.

The closing sentence on the previous article was “…this could be an edge case and maybe it might be made prettier, and more Jest-like in future iterations”. Not a strong statement, but I didn’t want to be too controversial; it’s a decent library. This statement was in reference to having to navigate a call and find what was sent to the client (for example on line 28 in the image below; s3ClientMock.call(0).args[0].input). This is what I want to cover first.

An additional library is needed to make it prettier. The library is called aws-sdk-client-mock-jest. It’s been published by the same author of the aws-sdk-client-mock and introduces custom Jest matchers to simplify tests. It does simplify the tests. Taking the example of s3ClientMock.call(0).args[0].input; it is now possible to use s3ClientMock. This is done using the .toHaveReceivedCommandWith matcher. This is great. The code looks clean and it’s easier to understand.

Below is the code for the test. Much of it is the same as the previous version in this article. Line 29 is where the difference is. This is using a matcher from the aws-sdk-client-mock-jest.

Slighty Annoying

One of the small gripes I have with it is that a command needs to be included in the assertion. Near the end of line 29 there is PutObjectCommand. This is what’s sent to the S3 client as the action that should be performed (examples for S3 could be CreateMultipartUploadCommand or GetObjectCommand). But we already know what this command is (on line 17). We had to use it in order for the test to work. It seems unnecessary to add it again

Another Slighty Annoying Thing

The second annoyance I have is that the error messages are still pretty cryptic in some places. Using the above example, we can remove the PutObjectCommand from line 29 so that it becomes:

        expect(s3ClientMock).toHaveReceivedCommandWith({
          Bucket: process.env.SHOPPING_BASKET,
          Key: item,
          Body: JSON.stringify(data)
        });

Running the test will then fail, and produce a horrible, uninformative message:

    Expected value to be equal to:
      true
    Received:
      false

One of the thing that I loved about the standard Jest library was that debugging the tests almost wrote the tests for you. It gave you a decent error message that you could work with.

Using the example test, changing the matcher to toHaveReceivedCommand (removing the With) and then running the test would still result in the above error message (Received: false). Not very helpful.

Contrast to the standard Jest library where using the wrong matcher for a scenario result’s in a delightful error message, an explanation and a suggested fix. The below example is changing a toEqual to toBe:

    expect(received).toBe(expected) // Object.is equality

    If it should pass with deep equality, replace "toBe" with "toStrictEqual"

    Expected: {"Body": "{\"id\":\"purchase\",\"note\":\"nourishment\"}", "Bucket": "shopping_basket", "Key": "beverages/full_cream_milk"}
    Received: serializes to the same string

Closing thoughts

Despite the bashing I have given this module in this article, it’s nice to work with. All of the AWS services that I use are covered by the aws-sdk-client-mock. The methods that can be chained such as .on, .rejects and .resolves are nice to work with. This is important because working with a library for creating automated tests is something that should be easy to use. The code should be readable and easy to understand. They are unit tests. So it should easy to understand the tests. And this module achieves that.

Saying that, I have seen this functionality overused. The below code is a beforeEach and it's setting up to resolve as well as reject.

  beforeEach(() => {
    awsMock.mock("S3", "headObject", mockHead);
    s3ClientMock
      .on(HeadObjectCommand, { Bucket: bucket, Key: data })
      .resolves(`arn:aws:s3:::${bucket}/${data}`)
      .on(HeadObjectCommand, { Bucket: bucket, Key: notFoundKey })
      .rejects("NotFound");
  });

The problem is that in each test that follows there’s a test for a resolve scenario and a test for a reject scenario. Both of these scenarios don’t need to be run before each test. It’s only a small example. But the point is that the test could get much larger and this will make it harder to understand what is happening in the test.

Overall, having the ability to write unit tests for AWS SDK v3 is awesome. It supports ES6 syntax (ie. const {mockClient} = require(‘aws-sdk-client-mock’);) and can be used with TypeScript. The documentation is easy to understand too and has good examples of how to create particular tests. The Github page can be found here. I’m not fully sold on aws-sdk-client-mock-jest either, but I think that’s because I’m too fond of the original Jest library.

The need to add a new module (aws-sdk-client-mock-jest) isn’t something that makes me happy. A additional module to be added to help with testing is just another module that someone will forget to add. However, I can understand the reasons it’s standalone; someone might not want to use the new Jest-like matchers, and not want the added bloat of things they are not using.

Image Credit

AWS
Aws Sdk
Jest
Unit Testing
Nodejs
Recommended from ReadMedium