Serverless integration testing

Home Blog Serverless integration testing

In this bite-sized tutorial, we look at how to add Cognito to your integration tests flow, making for true black-box testing.

Testing Cognito

We love integration tests here at Lumigo. We see them as a major part of our CI/CD process, and we believe that they play a pivotal role in serverless testing.

https://twitter.com/TServerless/status/1017040857663406080

One of the test scenarios that we have here is creating an event in our system and then executing an API REST query to retrieve the event details. It’s pure black box testing. Our REST interface sits behind API-GW and is authenticated by Cognito.

If you’ve ever tried to create users in Cognito programmatically, you know that it’s hard. This is because setting the initial password is not enough, the developer needs to change it manually  😨, but manual and automation do not go together.

The following article will cover:

  • Our testing scenario and how we incorporate Cognito into it
  • Creating a user in Cognito via a python script, including their password 😃
  • Actual testing code snippets

Testing scenario

So before digging into the details, let’s first define our test scenario:

  1. We need to create a valid Cognito user. At Lumigo the developer uses the same Cognito user to run the integration test and to log into our development dashboard.
  2. We then embed the username and password created into the integration test configuration.
  3. In the test itself, we pull the configuration and authenticate it using amazon-cognito-identity-js

Creating a user

One of the hardest things about using Cognito is to create a user with a predefined password, without the need to change it after first login (FORCE_CHANGE_PASSWORD account status).

Luckily, there is a nice Python package called warrant which gives us the ability to play directly with Cognito in Python. Let’s use it to create our user and define its password.

Let’s quickly go over it

  • We are using click for argument support
  • As arguments, we need the user pool ID and client ID
  • And you must supply the user’s email that will act as username
  • The magic happens in two places:
    • Line 66 – Create a Cognito user, this initial creation which leaves the user in FORCE_CHANGE_PASSWORD account status
    • Line 88 – Which changes the user’s password to the one we actually want

Embedding the user’s password and username

After creating the user we need to embed it into a configuration file that our integration tests framework uses. For the integration tests we use NodeJS, therefore our best way to pass configuration is to use dotenv. We’ve created a simple prepare_env.sh script which prepares the .env files.

The trick in the script is to automate everything, i.e.

  • Line 59 – Generate a random username.
  • Line 65,66 – Pull the pool and client ID automatically through AWS CLI
  • Line 73 – Creating virtual env and install requirements.txt

Authenticating with the user

After creating the user and embedding it in a .env file, it’s time to use it. Each test has the following structure:

  • Authenticate user via Cognito and receive an authentication token
  • Use the authentication token in the Authorization header

Let’s look at an example:

Important pointers:

  • We use amazon-cognito-identity-js
  • Usually, amazon-cognito-identity-js is used in the client, therefore, you need to set global.fetch for it to work, see line 3
  • Line 34 – at the end we are interested in the token itself which you can use in the Authorization header

To summarize the process:

  • Each developer prepares their development environment once by running a script
  • The script creates a Cognito user
  • The Cognito user is used by the integration tests to receive an authentication token
  • Which in turn is used in the authorization header

Tell us how you use Cognito in your integration flow! How do you solve the issues covered in this article? Agree or disagree with our approach? Share your thoughts on Twitter 🙂