Skip to content

Commit 3cef081

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]. I took out the explicit running of migrations because our integration tests should now cover that (as they do run migrations). [1] ThomWright/postgres-migrations#93 Issue #43 Support integration tests
1 parent d15b9fc commit 3cef081

10 files changed

+78
-7
lines changed

.github/workflows/test.yml

+5-5
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,16 @@ jobs:
3636
- name: Run build
3737
run: npm run build
3838

39-
- name: Run migrations
40-
run: npm run migrate
39+
- name: Run unit tests
40+
run: npm run test:unit -- --coverage
41+
42+
- name: Run integration tests
43+
run: npm run test:integration -- --coverage
4144
env:
4245
PGHOST: localhost
4346
PGPORT: 5432
4447
PGUSER: postgres
4548
PGPASSWORD: postgres
4649

47-
- name: Run tests
48-
run: npm run test -- --coverage
49-
5050
- name: Upload coverage
5151
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)