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 nyc
In
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
scripts
inpackage.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)