Unit testing TypeScript with Jest: Part Two — CI/CD pipeline setup with GitHub Actions
Tags: testing, devops, tutorial, github
Published: 2022-03-23 13:03:07

## Automate unit tests in a CI/CD pipeline with GitHub Actions

> This will be a continuation of my earlier article. You can continue to read to get to know the concepts of setting up a CI/CD pipeline with GitHub Actions. If you’d like to know more about setting up TypeScript with Jest, make sure to also read part 1.

We have already gone through the setup for running unit tests on your machine using Jest. Running these tests locally is just one step of the process. Ideally, you will want to streamline your software development by automating the execution of all the tests that you’ve written. Every time that you create new functionality for your business application, you want to verify that the tests that you’ve written are still rock-solid. If one of these tests fails, it probably means that your new logic is interfering with existing functionality. You can add extra conditions in your repository to enforce that new functionality can only be added if existing and new tests are successful. This way, we always know that when we do add new business rules to our application, it always contains a stable and test-verified version.

Nowadays most companies adopt the [Git workflow](https://www.atlassian.com/git/tutorials/comparing-workflows#:~:text=A%20Git%20workflow%20is%20a,in%20how%20users%20manage%20changes.) for their development cycle. This entails that there is a main branch in which everything is stable and tested. This also means that all versions that are on the main branch can also be used in a production environment. When a developer creates new functionality for an application, they will create a separate feature branch in which new functionality will be placed. In this feature branch, we want to verify that the application is stable by running all the tests. If these tests are successful, the merge is allowed into the main branch. But how do we approach this problem of automating the execution of these tests? How do we set up a pipeline to always run tests for new functionality? The answer to this is a CI/CD pipeline.

### What is a CI/CD pipeline?
A CI/CD pipeline is a method for automating different stages in the software development cycle. CI stands for Continuous Integration and CD for Continuous Deployment. The main purpose of CI/CD is to merge the development and operations cycle into one team so that problems for implementing new business logic can be solved by the same team in a much earlier phase.

Putting it into the context of our situation, this means that we want to set up a CI/CD pipeline that always runs in the feature branch and also in the main branch. We want to introduce ongoing automation and monitoring of the different stages of the lifecycle of our app. The test stage needs to be successful for the feature branch, and when this feature branch is merged into the main branch, we need to re-run this to verify that this is still the case.

### Where to run the CI/CD pipeline?
![Logo for GitHub Actions](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/wcnvxlnf5esfijazcskn.png)

Every code repository service has its own version of a CI/CD pipeline. It’s easier to run the pipeline at the same service where your source code is being hosted. Alternatively, you can also separate this by hosting your source code at one service and running the CI/CD pipeline in a separate service.

Examples of code repository services and their corresponding CI/CD implementations are
- [GitHub](https://github.com/) and [GitHub Actions](https://github.com/features/actions)
- [GitLab](https://about.gitlab.com/) and [GitLab CI](https://about.gitlab.com/stages-devops-lifecycle/continuous-integration/)
- [Azure DevOps](https://azure.microsoft.com/en-us/services/devops/#overview) and [Azure Pipelines](https://azure.microsoft.com/en-us/services/devops/pipelines/)

Other services for running purely the CI/CD pipelines are [Jenkins](https://www.jenkins.io/), [CircleCI](https://circleci.com/) and [Travis CI](https://travis-ci.org/).

<hr/>

### How to get started
We will be using GitHub to host our code and [GitHub Actions](https://github.com/features/actions) to implement the CI/CD pipeline since GitHub is one of the most well-known providers for open-source software development. The CI/CD pipelines for other competitors will require a different setup and their configurations can also differ.

Every CI/CD pipeline has costs attached to it. GitHub Actions has a very generous free tier for public repositories. If you’re using a public repository, GitHub Actions is completely free of charge. If you’re using private repositories, you are allowed to run 2000 minutes of your pipelines for free.

The walkthrough for this article will be a continuation of part one. If you haven’t done part one, you can also start with a clean setup with this [branch on GitHub](https://github.com/duncanlew/demo-jest-typescript/tree/part-2-clean-start). If you already have your own tests set up and would like to know, how to set up a CI/CD pipeline, other project dependencies might be needed. We need the following to get started:
- Hosted your source code with tests at GitHub
- IDE of choice (e.g.: Visual Studio Code)
- Optional: project dependencies depending on your setup

### 1. Create a feature branch
First things first, let’s create a feature branch called `github-ci-cd` to test out our first GitHub Actions workflow. You can do that by executing the following in your terminal:
```bash
git checkout -b github-ci-cd
```
This creates the feature branch and checks it out immediately.

### 2. GitHub Actions directory
Each GitHub Action workflow that we create will be a `yml` file. This `yml` file specifies which steps need to be taken to run our pipeline. All these workflows for GitHub Actions must be placed in the directory called `.github/workflows`. Let’s create the `main.yml` file by running the following in the terminal:
```bash
mkdir -p .github/workflows
touch .github/workflows/main.yml
```

### 3. Define a trigger event for the workflow
We need a trigger for our CI/CD pipeline to run. We might not necessarily want the pipeline to run for all events that occur in the repository. If you are working in your feature branch, you might be pushing a lot of commits, but running the pipeline for every commit may not be necessary. There can be caps on the number of pipelines that can be run simultaneously, and even costs depending on whether you exceed the quotas for your free tier. For this reason, we want to define when we want the pipeline to run. There are two specific instances that we can define:
- Run for every push event on the main branch

Every commit that is pushed on the main branch, needs to be verified.
- Run for every push in a pull request

If a pull request is created, every commit needs to be verified to know whether it is merge-ready

There may be other instances, in which you might want to run the pipeline. So feel free to modify it to fit your needs.

In your newly created `main.yml`, we need to define these triggers for GitHub Actions so that it understands the triggers. We can do that as follows:
{% gist https://gist.github.com/duncanlew/24834450dde8fd5a2b24da731b667183 %}

We have defined the `name` of the workflow and also defined the two use cases when we want the GitHub Action to be run. If you want to run the GitHub Actions for every push event, you can use this simplified event definition:
{% gist https://gist.github.com/duncanlew/b6f75a47ec652963007909300158ff98 %}

### 4. Define job and steps for GitHub Actions
The `main.yml` file also needs the definition for jobs and their corresponding steps. A job is simply a grouped workflow with steps that will be run sequentially. We can define multiple jobs which can be run in parallel. This can be the case for a build job and a test job. They don’t rely on one another and could be run in parallel.

We will focus on just creating one job for running our tests. We will define a job called `test-job` and it will contain the following four steps:
- Retrieve the code from our repository
- Download Node.js version 17 for GitHub Actions to run
- Install the required dependencies
- Run the tests

The first two actions will make use of automated actions that we can find from the [GitHub Marketplace](https://github.com/marketplace?type=actions). They are a predefined set of instructions that allow us to very easily check out our code and install the correct Node dependency without having to know the intricacies of the underlying virtual machines for GitHub Actions.

We are going to add the definition of the four steps for our `test-job`. Your `main.yml` file should look like this now:
{% gist https://gist.github.com/duncanlew/a900fe2f6011dbcbcbd78f3acadde598 %}

### 5. Create a pull request to see GitHub Actions in action
We have now created a fully functional `main.yml` file and will now take the next step to run our CI/CD pipeline for the first time. Make sure to commit all the changes that you’ve made for the main.yml file and then push it to GitHub:
```bash
git add .
git commit -m 'created main.yml file'
git push -u origin github-ci-cd
```
If you navigate to your repository in GitHub, you will see the following orange banner on the top:
![Yellow banner for creating a pull request after pushing the new feature branch](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9u7l2er5dpr9wb3t7lmu.png)

Click on **Compare & pull request**. You will be directed to a page to create your pull request. Give the pull request an appropriate name and then click on **Create pull request**:
![Create pull request page with title and creation button](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9ob9po50x91pal8k53do.png)
 
 

You will now be redirected to a page in which you can see the status of your pull request. If GitHub Actions is set up properly with the `main.yml` file, you will see that the CI/CD pipeline called `Demo GitHub Actions / test-job` is scheduled to be run.
![CI/CD pipeline is queued to be run after the creation of the pull request](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/0s1yr7aabkg7ve49y11z.png)
 
 
You can click on the **Details** link to navigate to the CI/CD pipeline to check the steps that you just defined in the main.yml file. That status page looks like this:
![A detailed page on the CI/CD pipeline](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jr9hq63r62lgsxajjck5.png)

The four steps that we have defined can be seen in the `test-job` and when we expand the `Test` step, we will see that all tests have been run successfully.

Navigating back to the status page of the pull request, we can see that all the checks of GitHub Actions are executed successfully and that the pull request can be merged without any problems:
![All checks have passed for the pull request](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6kxm5q39q1d7c78tv6wm.png)

### Conclusion
We have gone through the concepts of what CI/CD is in terms of the software development cycle. Nowadays it’s become an important part of each development phase to iterate quickly and get feedback on the work that you’ve delivered. Without setting up a CI/CD pipeline, this feedback would be delayed at a much later stage and might even be discovered by clients instead of by the developers themselves.

Using GitHub Actions to run a CI/CD pipeline was a very easy task and integrates very well with GitHub. We only needed to create one .yml file and place it in the right directory for GitHub Actions to get started. The next steps for you would be to modify this to fit the needs of your project. Happy coding to you all! 👋

The complete source code can be found [here](https://github.com/duncanlew/demo-jest-typescript).

If the content was helpful, feel free to support me [here](https://www.buymeacoffee.com/duncanlew):
<a href="https://www.buymeacoffee.com/duncanlew" target="_blank"><img src="https://cdn.buymeacoffee.com/buttons/default-orange.png" alt="Buy Me A Coffee" height="41" width="174"></a>