Skip to content

Commit 62348b2

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 ff930a0 commit 62348b2

File tree

9 files changed

+73
-2
lines changed

9 files changed

+73
-2
lines changed

README.md

Lines changed: 8 additions & 1 deletion
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

Lines changed: 1 addition & 0 deletions
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

Lines changed: 4 additions & 0 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 3 additions & 1 deletion
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",
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"
1719
},
Lines changed: 6 additions & 0 deletions
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

Lines changed: 5 additions & 0 deletions
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

Lines changed: 3 additions & 0 deletions
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

Lines changed: 40 additions & 0 deletions
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)