If you’re coming across this article, you likely have a good understanding of why automated testing is so crucial to the success of your business. However, if you’re unfamiliar with the benefits, automated testing will save you money and time, increase application reliability, and increase team productivity. In some extreme yet real-world scenarios, it could even save you from losing hundreds of thousands of dollars in revenue by detecting a fatal bug before it makes its way into production. Reaping the benefits of automated testing is easier than you might think. Here in this blog, I’ll show you just how simple it is to safeguard your JavaScript application utilizing the Jest testing framework, AWS CodeBuild, and AWS CodePipeline.
Benefits of Jest
Jest is a lightweight, simple JavaScript testing framework. It's open-source and written by the same Facebook open-source team that brought us React. It's straightforward and intuitive to use, allowing developers to focus more on building business logic rather than an intricate and hard-to-manage testing framework. It can be used to test applications written in JavaScript, TypeScript, Vue, Angular, and more, but most importantly, for our use case, React.
- Jest is incredibly simple to set up. We'll get into how exactly to do this in more detail below.
- Jest is fast and efficient. It runs the slowest tests first to maximize core utilization and uses advanced caching techniques and parallelization. Looking at a real-world example, Airbnb switched from Mocha to Jest, and were able to reduce their tests run time to take only ⅓ of the time, improving from 45 mins to 15 mins.
- Jest provides an easy-to-understand API that enables engineers to write tests in an intuitive, human-readable way.
- Jest offers comprehensive, up-to-date documentation that simplifies learning and mastery.
- Jest has a vast user base that shares ideas, best practices, and troubleshooting assistance.
Capabilities of Jest
Jest is a truly robust tool that, if used right, will dramatically improve application reliability and the developer experience. It does more than simply allowing users to write unit and integration tests. Jest has a plethora of baked-in capabilities and features that allow developers to get the most out of their time when writing tests. The following are some of the most important capabilities of Jest that truly make a difference when it comes to safeguarding your application's reliability and user experience:
- Robust and straightforward configuration. Jest’s out-of-the-box config is sensible and works great for most projects, but if you need more customization there are over 70 different configuration options to craft a version of Jest that perfectly meets your project’s requirements.
- A long, helpful list of built-in matchers. These matchers allow developers to easily check conditions in an easy human-readable way. For example, checking if a variable is true is as simple as writing the following:
-
Built-in
snapshot testing. Snapshots allow developers to capture the output of components or data
structures and save it as a snapshot file. Then, when the tests are run
again, after that initial snapshot is saved, they will compare the output of
the current test to the saved snapshot file. They are incredibly useful in
many scenarios, but one of the most impactful is determining that your
underlying code changes didn’t have any unintended UI changes. Writing
snapshot tests is as simple as rendering a component with a library like
React Test Renderer
and calling the Jest snapshot checker function. The following code snippet
shows how simple it is:
- Robust mocks. Jest allows developers to mock functions, modules, and external dependencies. This can be crucial for isolating components that you want to test, ensuring that they are evaluated in isolation without relying on actual external resources. This can also speed up testing in cases where a developer doesn’t want to wait for an expensive call out to external dependencies when they know what the return will look like. If you don’t want to explore the documentation but want more ideas or code examples, the following blog is a good place to start.
- Function spying. Spying on functions allows developers to track function calls and track how they are used in execution. For an example, look at the following code snippet:
- Asynchronous testing support. Jest allows developers to seamlessly test asynchronous code. They can use built-in JavaScript asynchronous standards like async/await and promises or take advantage of Jest's automatic resolution of promises, even further simplifying the testing of asynchronous behavior.
- Code coverage analysis. After running tests, Jest will generate a detailed coverage report that indicates which lines of code were executed during the tests. This allows developers to identify areas of code that are not adequately tested and improve the overall code quality. This can be displayed in the command line, but developers can also choose to generate a renderable html file that outlines test coverage with an easy-to-use UI.
- Watch mode. When this valuable productivity feature is enabled, Jest monitors file changes and re-runs only the relevant tests impacted by those changes. This real-time feedback allows developers to iterate quickly during development without running the entire test suite each time. Developers could also just run a test file individually, but this could lead them to miss tests they are unaware of that are impacted by their code changes.
- Impressive plugin and extension support. Jest's ecosystem includes numerous plugins and extensions that enhance its functionality. You can view a list of valuable plugins here. A great example is jest-email-reporter which can send emails when a test fails, which could be used with CICD to notify developers when tests are failing in a deployment to the production environment.
Writing a Jest Test For a React Component
Consider a simple React component rendering that displays a name passed in as a prop. We want to verify that the name is making its way into the component and rendering correctly in the Document Object Model (DOM). The following steps show how simple it is to set up a test with Jest to mimic this behavior. Keep in mind these libraries are ever-changing, and this tutorial will reference only a few of the libraries you may want to explore. This overview will give you an idea of how easy it is to write Jest tests and you can then pursue more complex examples based on your testing needs and goals. For complete documentation, check out the official Jest framework guides.
- Installing Dependencies
First, we need to install the following dependencies to make things work: React, React DOM, React Testing Library, and Jest. To do this, run the following commands in the terminal at your project directory.
- Creating a React Component
We first create a React component that takes in a name and displays it after
"Your name is:" if the name is defined. If the name is undefined, it renders,
"You have no name!". Lastly, we give the component a test id to easily access
it within our tests.
- Writing a Jest Test
Typically Jest testing files are denoted with ‘.test.’ or ‘.spec.’. They can
be placed in the same folder as the page or component you are testing or in a
separate test folder. All of this can be configured within your
Jest configuration file.
First, we will import the React component DisplayName and
React Testing Library. We will use React Testing Library to render our component DisplayName.
React Testing Library provides lightweight utility functions that we can call
on top of
React DOM
to test our components.
We will then use Jest to describe and configure the test. In the test, we will
use functions from React Testing Library and Jest to check if the component
displays the name correctly. We will also write a second test that checks the
condition where the name is undefined.
The first string passed to test( ) will be displayed in the console when
running the tests with Jest. In the case of a test failure, this provides a
way to know which test failed.
Capabilities and Benefits of AWS CodeBuild & AWS CodePipeline
AWS CodeBuild
is a fully managed build service in the cloud meaning you don’t have to
configure any of the underlying resources, AWS does it all for you. It takes
the hassle out of compiling source code, running unit tests, and generating
deployment-ready artifacts. By removing the burden of provisioning, managing,
and scaling your own build servers, CodeBuild allows developers to focus
solely on writing code and accelerating the software delivery lifecycle. The
following are just some of the benefits and capabilities of AWS CodeBuild:
-
Extensive language and platform support. It offers a slew of pre-configured
build environments and allows developers to create custom build environments
tailored to the specific requirements of their application.
-
CodeBuild seamlessly integrates with other AWS services. This lends to easy
monitoring and automated deployments, which we will explore further later.
This can be especially beneficial if your application is already on AWS.
-
Builds are secure and isolated, with each build being run in a new container
minimizing potential security risks.
-
CodeBuild
build specs
allow developers to define their build steps with YAML alongside their code
in their version control system, reducing complexity. It allows developers
to easily roll back changes, keep things consistent between builds, and even
reuse configuration files on similar projects.
-
CodeBuild supports
multiple inputs sources and multiple output artifacts, allowing developers to build multiple resources in tandem.
-
Your secret keys stay within the AWS ecosystem. You don't need to expose
them to an external repository or provider to trigger a build.
-
CodeBuild uses the pay-as-you-go model, meaning you only pay for what you
use. There are no upfront introductory fees or commitments. If you have a
small application or POC, your usage might even fall under
AWS Free Tier.
AWS CodePipeline
is a powerful continuous integration and deployment tool that seamlessly
orchestrates the entire software release process. CodePipeline automates the
building, testing, and deployment of your applications, leading to quicker and
more reliable application and infrastructure updates. Automation, as discussed
in the introduction, is crucial for modern-day projects and businesses, and
CodePipeline makes it easy to implement. The following explains some of the
benefits and capabilities of AWS CodePipeline:
-
CodePipeline, just like Codebuild, is also a managed service, meaning that
you do not need to worry about any underlying infrastructure to run
CodePipeline. AWS automatically scales and creates resources as needed.
-
CodePipeline integrates with multiple AWS services and 3rd party tools,
creating a seamless CI/CD experience for developers. For instance, you can
use
AWS Identity Access Management
service roles to access AWS resources—no need to use or manage keys.
-
CodePipeline allows for
real-time monitoring and advanced logging
to provide valuable insights into pipeline health and progress.
-
CodePipeline automates your deployment, but still allows for manual approval
on critical steps that need human oversight.
- CodePipeline also uses the pay-as-you-go mode like CodeBuild.
-
CodePipeline supports
multiple deployment strategies
for CI/CD, such as blue/green deployments, ensuring smooth and seamless
rollouts with minimal downtime and user disruption.
Automating your Jest tests with AWS CodeBuild & AWS CodePipeline
Now that we have a Jest test setup, we’ll want to automate it within our
application build pipeline. That's where CodeBuild and CodePipeline come in.
The following guide shows how easy it is to set up automated Jest testing in
CodeBuild and CodePipeline. There can be some nuances in setup depending on
where the repository is kept, such as in
AWS CodeCommit,
GitLab, or
GitHub. You may also see subtle differences based on the application's
architecture, different build steps the application may have, and more, so
keep in mind that each application will likely have slightly different setup
steps. Jest has many other configuration options like generating coverage
reports, excluding certain file types and paths in your directory, setting up
global variables, and more, which could warrant its own blog post.
Additionally, there are multiple ways to configure CodeBuild and CodePipeline.
You can use the console with an interactive UI or set things up
programmatically using AWS
CLI,
SDKs, or
CloudFormation. There is a flavor combination to meet any need and comfort level. This
guide isn't meant to be an all-encompassing tutorial, but the example below
will show how simple automated Jest testing is to set up from scratch or drop
into your existing CodeBuild and CodePipeline configuration.
- Git Repository
Create or utilize an existing Git repository that stores the React application
code.
- Jest Configuration and Tests
Install and configure Jest in the React application. Write at least one test
and manually run it locally to ensure it works as intended. Add the test
script to package.json. Below is an example of the scripts in package.json for
a NextJS React application with Jest. The "test" script would be used locally,
and "test:ci" will be used in CodeBuild.
- Configure Build Specifications
Create a buildspec.yml file in the root of your project directory. This file
contains the build instructions for CodeBuild. It gives commands to install
dependencies, build the React application, and run Jest tests. Below is an
example buildspec.yml file:
- Configure AWS CodeBuild
Create a CodeBuild project in the AWS Management Console. Configure the source code settings to connect to the Git repository and specify the build environment, including the operating system and runtime. Elect to use the buildspec.yml file that was created in step 3. Enable CloudWatch logs or S3 logs to allow logging for your build. The logs are where we see the output of the build, for instance, the output from Jest indicating all passing and failing tests.
- Create a new pipeline using CodePipeline
Create a pipeline using CodePipeline in the AWS Management Console. As shown
in the screenshots below, name your pipeline, create a new service role,
specify the source as your Git repository, and configure the CodeBuild project
created in step 3 as the build stage action. Once completed, a source stage
will pull the code into the pipeline and build stage, where the application is
built and tested. We will skip the deploy stage in this tutorial, but this is
where you would put your application on a host to make it accessible to users.