It is easy to fall into a mindset when we are coding. When writing tests, we often need to consider various situations and make us re-examine our code, which is called Test-Driven-Development.
Writing tests can bring us certain benefits:
To verify the correctness of the code
To avoid errors when modifying code
To avoid errors when other team members modify your code
To facilitate automated testing and deployment
As your app grows larger, it becomes more complex, harder to maintain, and more buggy. Therefore, it's essential to write maintainable and testable code at very early start.
Coverage reports tell you how much code the test cases cover. It provides useful metrics that can help you assess the quality of your test suite. We then can use CodeClimate to analyze the quality of our code from the coverage reports.
Prerequisites
Suppose you have already created a React+Node JS app with npm create-react-app and npm init, setup express, and maybe have written some routes.
Likely, you will have a similar folder hierarchy in your monorepo as follows:
client
| public
| src
| App.js
| index.js
| app.test.js
| setupTests.js
| reportWebVitals.js
| package.json
server
| routes
| sample.route.js
| package.js
Tesing your react frontend with Jest and React Testing Library
If you are using create-react-app, both Jest and React Testing Library are built-in, so we don't need to install them separately.
By default, Jest will look for files with the .test.js suffix and files with the .js suffix in __tests__ folders.
An example app.test.js for testing components can be:
import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
it('renders welcome message', () => {
render(<App />);
expect(screen.getByText('Learn React')).toBeInTheDocument();
});
Testing your node backend with Jest and supertest
To start, you will need to first install the following packages by npm i jest supertest.
jest: Jest is a JavaScript testing framework built on top of Jasmine. Unit testing is the main usage of it.supertest: Supertest is a SuperAgent driven library for testing HTTP servers. With wich, we can test endpoints and routes.
Then in package.json, add
"scripts": {
...
"test": "jest"
},
We will write a sample route first.
In server/routes/sample.test.js, suppose we have two functions for getting all users and add user.
const express = require("express")
const router = express.Router()
router.get("/getUsers",getUsers)
router.post("/createUser",createUser)
module.exports = router
And in server/tests/sample.test.js
const request = require("supertest");
const express = require("express");
const app = express();
app.use(express.json());
app.use("/", router);
describe("GET /getUsers", () => {
it("should return all users", async () => {
const res = await request(app).get("/getUsers");
expect(res.statusCode).toBe(200);
expect(res.body.length).toBeGreaterThan(0);
});
});
describe("POST /createUser", () => {
it("should not create with empty name", async () => {
const req = {name: "",password: "123"};
const res = await request(app).post("/createUser").type('json').send(req);
expect(res.statusCode).toBe(400);
});
it("should create a user ", async () => {
const req = {name: "user",password: "123"};
const res = await request(app).post("/createUser").type('json').send(req);
expect(res.statusCode).toBe(200);
expect(res.body.name).toBe("user");
});
});
Testing Express+Mongodb
If you are testing with a mongodb, you should always make sure your database is seperate, that the datasets for each test don't interfere with each other. I would recommand using local mongodb.
You should put your mongodb url in .env and import dotenv to get the environment variable. Install dotenv by npm i dotenv.
In server/.env
MONGODB_URI="mongodb://localhost:27017"
You'll need to connect and disconnect the database before and after each test.
In server/tests/mongo.test.js
const mongoose = require("mongoose");
const request = require("supertest");
const app = require("../app");
require("dotenv").config("../.env");
beforeEach(async () => {
await mongoose.connect(process.env.MONGODB_URI);
});
afterEach(async () => {
await mongoose.connection.dropDatabase();
await mongoose.connection.close();
});
Generate test coverage report
In the Code Climate documentation, the supported coverage tool for JavaScript is lcov (generated by Istanbul).
In both client and server folders:
At root, add the nyc package with
npm install --save-dev nycIn
package.json, add:
"jest": {
"coverageReporters": [
[
"lcov",
{
"projectRoot": ".."
}
]
]
},
"nyc": {
"reporter": [
"lcov"
]
}
To make jest and nyc generate lcov reports. You can also add other reporters like text, text-summary.
-
For your react frontend, add to the
package.json:
"scripts": {
...
"coverage": "react-scripts test --coverage --watchAll=false"
},
...
"jest": {
...
"transformIgnorePatterns": ["node_modules\/(?!axios)"],
"collectCoverageFrom": [
"src/**/*.js",
"!**/node_modules/**",
]
}
At the root of client folder, run npm coverage to see the coverage report.
-
Similarly in the node backend, add to the
scriptsinpackage.json:
"scripts": {
...
"coverage": "jest --coverage"
},
...
"jest": {
...
"transformIgnorePatterns": ["node_modules\/(?!axios)"],
"testEnvironment": "node",
"collectCoverageFrom": [
"**/routes/**/*.js",
"!**/node_modules/**",
]
}
At the root of server folder, run npm coverage to see the coverage report.
We can use the property collectCoverageFrom to specify which files need to be collected, and which files should not be collected (node_modules, config.js, datasets, and so on).
Upload coverage report to CodeClimate
Note that you need at least some test coverage to generate a report that is able to be uploaded to CodeClimate, so make sure you followed the previous parts carefully.
To start, make sure you already have a CodeClimate account.
On the dashboard, click on "Add a repository" and then "add repo" to link your repo to CodeClimate.
Go to repo setting/test coverage and find the TEST REPORTER ID
In your Github repo, click on setting/secrets/action, and add the CC_TEST_REPORTER_ID as a new secret key.
Below is an example of the github-action.yml
name: build
on:
workflow_dispatch:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
Coverage:
runs-on: ubuntu-latest
timeout-minutes: 10
strategy:
matrix:
node-version: [14.x]
steps:
- uses: actions/checkout@v2
- uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- name: Test Coverage Frontend
run: |
cd client
npm install
npm coverage
- name: Test Coverage Backend
run: |
cd server
cp -r .env.example .env
npm install
npm coverage
- name: Test & publish code coverage
uses: paambaati/codeclimate-action@v3.2.0
env:
CC_TEST_REPORTER_ID: ${{secrets.CC_TEST_REPORTER_ID}}
with:
coverageLocations: |
${{github.workspace}}/client/coverage/lcov.info:lcov
${{github.workspace}}/server/coverage/lcov.info:lcov
In order to upload the two coverage tests for react and node at once, we need to set the projectRoot of the lcov coverage reporter of jest to the root of our monorepo (using ".." in package.json).
Top comments (0)