Skip to content

Commit e8bbef0

Browse files
committed
Support integration tests
Integration tests are slower than unit tests because they rely on a database connection, so we want the ability to run them separately depending on our environment. Jest allows us to define different config files. By creating a `base` config we can define all of the common settings between unit and integration tests, and then customize for each type in the respective extended configs. In addition to configuration this begins to define define some hooks so that we can eventually run these tests in parallel. Unfortunately there is a bug in the migration library we're using which prevents that kind of parallel migration within a single database / across multiple schemas. We have an open PR with a patch for that bug[1]. [1] ThomWright/postgres-migrations#93 Issue #43 Support integration tests
1 parent d15b9fc commit e8bbef0

10 files changed

+83
-4
lines changed

.github/workflows/test.yml

+10-2
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,16 @@ jobs:
4444
PGUSER: postgres
4545
PGPASSWORD: postgres
4646

47-
- name: Run tests
48-
run: npm run test -- --coverage
47+
- name: Run unit tests
48+
run: npm run test:unit -- --coverage
49+
50+
- name: Run integration tests
51+
run: npm run test:integration -- --coverage
52+
env:
53+
PGHOST: localhost
54+
PGPORT: 5432
55+
PGUSER: postgres
56+
PGPASSWORD: postgres
4957

5058
- name: Upload coverage
5159
uses: codecov/codecov-action@v2

README.md

+8-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,14 @@ In order to run this software you need to set up a [Postgres 14](https://www.pos
2424
edit .env
2525
```
2626

27-
3. Run migrations
27+
3. Set up test environment variables
28+
29+
```bash
30+
cp .env.example .env.test
31+
edit .env.test
32+
```
33+
34+
4. Run migrations
2835

2936
```bash
3037
npm run migrate:dev

jest.config.js renamed to jest.config.base.js

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module.exports = {
66
},
77
collectCoverageFrom: ["src/**/*.ts"],
88
preset: 'ts-jest',
9+
setupFiles: ['<rootDir>/src/test/setupEnv.ts'],
910
testEnvironment: 'node',
1011
testPathIgnorePatterns: ["<rootDir>/dist/"],
1112
silent: true,

jest.config.int.js

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
var config = require('./jest.config.base.js');
2+
config.testMatch = ['**/?(*.)+(int).(spec|test).[jt]s?(x)'];
3+
config.setupFilesAfterEnv = ["<rootDir>/src/test/integrationSuiteSetup.ts"];
4+
module.exports = config;

jest.config.unit.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
var config = require('./jest.config.base.js');
2+
config.testMatch = ['**/?(*.)+(unit).(spec|test).[jt]s?(x)'];
3+
module.exports = config;

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@
1111
"lint": "eslint ./src --ext .ts",
1212
"migrate": "node -r dotenv/config dist/scripts/migrate.js",
1313
"migrate:dev": "ts-node -r dotenv/config src/scripts/migrate.ts | pino-pretty",
14-
"test": "jest",
14+
"test": "npm run test:unit && npm run test:integration",
15+
"test:unit": "jest --config=jest.config.unit.js",
16+
"test:integration": "jest --config=jest.config.int.js --runInBand",
1517
"start": "node dist/index.js",
1618
"start:dev": "ts-node src/index.ts | pino-pretty"
1719
},
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,20 @@
11
import request from 'supertest';
22
import { app } from '../app';
3+
import {
4+
prepareDatabaseForCurrentWorker,
5+
cleanupDatabaseForCurrentWorker,
6+
} from '../test/utils';
37

48
const agent = request.agent(app);
59

610
describe('/canonicalFields', () => {
711
describe('/', () => {
812
it('should return HTTP Status Code 200 OK', async () => {
13+
await prepareDatabaseForCurrentWorker();
914
await agent
1015
.get('/canonicalFields')
1116
.expect(200);
17+
await cleanupDatabaseForCurrentWorker();
1218
});
1319
});
1420
});

src/test/integrationSuiteSetup.ts

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { db } from '../database';
2+
3+
afterAll(async () => {
4+
await db.close();
5+
});

src/test/setupEnv.ts

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import dotenv from 'dotenv';
2+
3+
dotenv.config({ path: '.env.test' });

src/test/utils.ts

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import {
2+
db,
3+
migrate,
4+
} from '../database';
5+
6+
const generateSchemaName = (workerId: string): string => `test_${workerId}`;
7+
8+
const getSchemaNameForCurrentTestWorker = (): string => {
9+
if (process.env.NODE_ENV !== 'test') {
10+
throw new Error('You cannot get a test schema name outside of a test environment.');
11+
}
12+
if (process.env.JEST_WORKER_ID === undefined) {
13+
throw new Error('You cannot get a test schema name if jest has not specified a worker ID.');
14+
}
15+
return generateSchemaName(process.env.JEST_WORKER_ID);
16+
};
17+
18+
const createSchema = async (schemaName: string): Promise<void> => {
19+
await db.query(`CREATE SCHEMA IF NOT EXISTS ${schemaName};`);
20+
};
21+
22+
const setSchema = async (schemaName: string): Promise<void> => {
23+
await db.query(`SET search_path TO ${schemaName};`);
24+
};
25+
26+
const dropSchema = async (schemaName: string): Promise<void> => {
27+
await db.query(`DROP SCHEMA ${schemaName} CASCADE;`);
28+
};
29+
30+
export const prepareDatabaseForCurrentWorker = async (): Promise<void> => {
31+
const schemaName = getSchemaNameForCurrentTestWorker();
32+
await createSchema(schemaName);
33+
await setSchema(schemaName);
34+
await migrate();
35+
};
36+
37+
export const cleanupDatabaseForCurrentWorker = async (): Promise<void> => {
38+
const schemaName = getSchemaNameForCurrentTestWorker();
39+
await dropSchema(schemaName);
40+
};

0 commit comments

Comments
 (0)