5.2 KiB
title | description | position | category | menuTitle |
---|---|---|---|---|
Unit Test | How to write unit tests | 3300 | Engineering | Unit Test |
Key points
- We use Mocha as our test runner and chai as our assertion library.
- We use Supertest to test our API endpoints.
- All individual unit tests are independent of each other. We don't use any shared state between tests.
- Test environment also includes
sakila
database and any change onsakila
by a test is reverted before running other tests. - While running unit tests, it tries to connect to mysql server running on
localhost:3306
with usernameroot
and passwordpassword
(which can be configured) and if not found, it will usesqlite
as a fallback, hence no requirement of any sql server to run tests.
Walk through of writing a unit test
We will create an Table
test suite as an example.
Describing test suite
Create Table
test suite by using describe
function of mocha.
export default function () {
describe('Table', tableTests);
}
Configure test
We will configure beforeEach
which is called before each test is executed. We will use init
function from nocodb/packages/tests/unit/init/index.ts
, which is a helper function which configures the test environment(i.e resetting state, etc.).
init
does the following things -
- It initializes a
Noco
instance(reused in all tests). - Restores
meta
andsakila
database to its initial state. - Creates the root user.
- Returns
context
which hasauth token
for the created user, node server instance(app
), anddbConfig
.
We will use createProject
and createProject
factories to create a project and a table.
let context;
beforeEach(async function () {
context = await init();
project = await createProject(context);
table = await createTable(context, project);
});
Test case
We will use it
function to create a test case. We will use supertest
to make a request to the server. We use expect
(chai
) to assert the response.
it('Get table list', async function () {
const response = await request(context.app)
.get(`/api/v1/db/meta/projects/${project.id}/tables`)
.set('xc-auth', context.token)
.send({})
.expect(200);
expect(response.body.list).to.be.an('array').not.empty;
});
Integrating the new test suite
We create a new file table.test.ts
in packages/nocodb/tests/unit/rest/tests
directory.
import 'mocha';
import request from 'supertest';
import init from '../../init';
import { createTable, getAllTables } from '../../factory/table';
import { createProject } from '../../factory/project';
import { defaultColumns } from '../../factory/column';
import Model from '../../../../src/lib/models/Model';
import { expect } from 'chai';
function tableTest() {
let context;
let project;
let table;
beforeEach(async function () {
context = await init();
project = await createProject(context);
table = await createTable(context, project);
});
it('Get table list', async function () {
const response = await request(context.app)
.get(`/api/v1/db/meta/projects/${project.id}/tables`)
.set('xc-auth', context.token)
.send({})
.expect(200);
expect(response.body.list).to.be.an('array').not.empty;
});
}
export default function () {
describe('Table', tableTests);
}
We can then import the Table
test suite to Rest
test suite in packages/nocodb/tests/unit/rest/index.test.ts
file(Rest
test suite is imported in the root test suite file which is packages/nocodb/tests/unit/index.test.ts
).
Running test
To run tests, run npm run test:unit
in packages/nocodb
directory.
NOTE: We can also run individual test by using
.only
indescribe
orit
function and the running the test command.
it.only('Get table list', async () => {
Folder structure
The root folder for unit tests is packages/tests/unit
rest
folder contains all the test suites for rest apis.model
folder contains all the test suites for models.factory
folder contains all the helper functions to create test data.init
folder contains helper functions to configure test environment.index.test.ts
is the root test suite file which imports all the test suites.TestDbMngr.ts
is a helper class to manage test databases (i.e. creating, dropping, etc.).
Patterns to follow
-
Factories
- Use factories for create/update/delete data. No data should be directly create/updated/deleted in the test.
- While writing a factory make sure that it can be used with as less parameters as possible and use default values for other parameters.
- Use named parameters for factories.
createUser({ email, password})
- Use one file per factory.
Using sakila db
To use sakila db use createSakilaProject
from factory/project
to create a project. This project will be seeded with sakila
tables.
Configurations
- For Mysql db configuration, use the following environment variables to configure.
DB_USER
: mysql usernameDB_PASSWORD
: mysql passwordDB_HOST
: mysql hostDB_PORT
: mysql port