2

API with NestJS #96. Running unit tests with CI/CD and GitHub Actions

 1 year ago
source link: https://wanago.io/2023/02/20/api-nestjs-tests-ci-cd-github-actions/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

API with NestJS #96. Running unit tests with CI/CD and GitHub Actions

We value your privacy
We use cookies on our website. We would like to ask for your consent to store them on your device. We will not run optional cookies until you enable them. If you want to know more about how cookies work, please visit our Privacy Policy page.

API with NestJS #96. Running unit tests with CI/CD and GitHub Actions

Docker NestJS

February 20, 2023
This entry is part 96 of 96 in the API with NestJS

We should closely monitor the state of tests in our NestJS application. In a few previous parts of this series, we’ve used Docker and GitHub actions to design a CI/CD pipeline that automates our deployments. In this article, we avoid deploying faulty code by running unit tests before each pull request automatically.

Preparing the Docker image

The first step is ensuring that our tests can run in our Docker image. To verify that, let’s look at the Dockerfile we’ve built so far.

Dockerfile
# Installing dependencies:
FROM node:18-alpine AS install-dependencies
WORKDIR /user/src/app
COPY package.json package-lock.json ./
RUN npm ci --omit=dev
COPY . .
# Creating a build:
FROM node:18-alpine AS create-build
WORKDIR /user/src/app
COPY --from=install-dependencies /user/src/app ./
RUN npm run build
USER node
# Running the application:
FROM node:18-alpine AS run
WORKDIR /user/src/app
COPY --from=install-dependencies /user/src/app/node_modules ./node_modules
COPY --from=create-build /user/src/app/dist ./dist
COPY package.json ./
CMD ["npm", "run", "start:prod"]

If you want to see how we’ve built the above configuration, check out the following articles:

We would fall into a significant pitfall if we used the above Docker image to run tests.

To install the dependencies, we run npm ci --omit=dev. Adding the --omit=dev flag tells NPM to skip installing all of the libraries listed in devDependencies. However, this is typically where we list libraries such as Jest that we need for testing.

package.json
"devDependencies": {
  "jest": "^29.3.1",
  "eslint": "^8.31.0",
  "eslint-config-prettier": "^8.6.0",
  "eslint-plugin-import": "^2.27.4",
  "prettier": "^2.8.3"
  // ...

Let’s modify our configuration to accommodate that.

Dockerfile
# Installing dependencies:
FROM node:18-alpine AS install-dependencies
WORKDIR /user/src/app
RUN npm install -g [email protected]
COPY package.json package-lock.json ./
RUN npm ci
COPY . .
# Creating a build:
FROM node:18-alpine AS create-build
WORKDIR /user/src/app
RUN npm install -g [email protected]
COPY --from=install-dependencies /user/src/app ./
RUN npm run build
USER node
# Running the application:
FROM node:18-alpine AS run
WORKDIR /user/src/app
RUN npm install -g [email protected]
COPY --from=install-dependencies /user/src/app/node_modules ./node_modules
COPY --from=create-build /user/src/app/dist ./dist
COPY package.json ./
RUN npm prune --production
CMD ["npm", "run", "start:prod"]

Since we need the packages listed in devDependencies to run our unit tests, we install all of them during the install-dependencies stage by skipping the --omit=dev flag.

This would make the resulting Docker image heavier if not for a modification to the run stage. By adding the npm prune --production command at the bottom, we eliminate the libraries from devDependencies before making the final Docker image.

Thanks to doing all of the above, the result of the create-build stage in the Dockerfile has all of the dependencies, but they are stripped when no longer necessary.

We also add RUN npm install -g [email protected] to ensure the latest version of NPM and avoid warnings in the terminal.

Running tests locally

We must run our tests with the Docker image targeting the create-build stage before removing the devDependevies. Therefore, let’s use the --target flag to build the image without running the last stage from our Dockerfile.

docker build -t nestjs-api:test --target=create-build .

When we look at the create-build stage in our Dockerfile, we can see that it doesn’t have the CMD keyword. When running the image, we must provide Docker with the desired command. Let’s run npm run test inside the shell of a Docker container running with our image.

docker run nestjs-api:test sh -c 'npm run test'

PASS src/authentication/authentication.service.test.ts
The AuthenticationService
when creating a cookie
✓ should return a string (17 ms)

Test Suites: 1 passed, 1 total
Tests: 1 passed, 1 total
Snapshots: 0 total
Time: 2.662 s
Ran all test suites.

Running Prettier

When checking the formatting, we usually set up Prettier using the --write flag to overwrite our files.

"scripts": {
  "format": "prettier --write \"src/**/*.ts\"",
  // ...

Instead, we want to be notified when our code does not meet the formatting standards. To do that, we need the --list-different flag.

"scripts": {
  "format:verify": "prettier --list-different \"src/**/*.ts\"",
  // ...

If a particular file is not formatted correctly when running the above command, Prettier lists it. It also uses the 1 exit code to signify an error.

Let’s rebuild our Docker image to include the above command.

docker build -t nestjs-api:test --target=create-build .

Now, we can run Prettier in our Docker container to verify our code.

docker run nestjs-api:test sh -c 'npm run format:verify'

Running ESLint

The official NestJS starter repository comes with ESLint configured and ready to go. We can easily use it with our existing configuration.

docker run nestjs-api:test sh -c 'npm run lint'

Running the tests with GitHub Actions

In the previous part of this series, we created a GitHub Actions workflow that runs every time we push our change to the master branch. Before that happens, we usually make a pull request to merge our changes. Let’s create a new GitHub Actions workflow that runs every time a PR is created.

.github/workflows/test.yml
name: Test the application
on: [pull_request]
  IMAGE_TAG: nestjs-api
jobs:
  test:
    name: Run tests
    runs-on: ubuntu-latest
    steps:
      - name: Check out the repository
        uses: actions/checkout@v3
      - name: Build the Docker image
        run: docker build -t $IMAGE_TAG --target=create-build .
      - name: Run Prettier
        run: docker run $IMAGE_TAG sh -c 'npm run format:verify'
      - name: Run ESLint
        run: docker run $IMAGE_TAG sh -c 'npm run lint'
      - name: Run tests
        run: docker run $IMAGE_TAG sh -c 'npm run test'

Thanks to adding on: [pull_request] above, there is a set of steps happening above every time we create a pull request:

  1. For GitHub Actions to access our code, we need to check out our repository. To do that, we use the actions/checkout@v3 action provided by GitHub.
  2. We build the Docker image targeting the create-build stage.
  3. Since our environment is ready, we run Prettier, ESLint, and unit tests.

Let’s create a Pull Request and verify that everything works as expected.

Screenshot-from-2023-02-19-22-28-10.png

We can see the information saying that “all checks have passed“. When we click “Details”, we can see more job information.

Screenshot-from-2023-02-19-22-30-36.png

Protecting our master branch

Even though we have the tests in place, nothing stops us from disregarding them and merging the changes even if they fail. Let’s change that.

To do that, we need to open our GitHub repository and go to Settings -> Branches. Here, we can add a branch protection rule prohibiting merging changes if our status checks haven’t succeeded.

Screenshot-from-2023-02-19-22-35-06.png

Summary

In this article, we established a process of testing our application whenever we want to merge new changes to the master branch. To do that, we had to modify our Docker image slightly to install all necessary dependencies and remove them when they were no longer needed. We’ve also created a new GitHub Actions workflow to run tests on every pull request.

Thanks to the above, we couldn’t merge our changes if any of our tests failed. This gives us an additional layer of security and prevents us from deploying a faulty version of our application.

Series Navigation<< API with NestJS #95. CI/CD with Amazon ECS and GitHub Actions
Subscribe
guest
Label
1 Comment
Oldest
Ken
1 day ago

You are amazing bro! Thanks for your awesome work.

Reply

wpDiscuz


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK