I have 2 db, one for development, one for testing. I want to connect to test db when running jest test, I set up 2 .env config, development use .env, testing use .env.test. But unit test can't connect db through connection.ts.
Update directory schema
app
├── .env
├── .env.test
├── loadEnv.ts
├── package.json
├── ormconfig-cli.ts
├── src
│ ├── connection.ts
│ ├── app.module.ts
│ ├── user
│ │ ├── entities
│ │ │ └── user.entity.ts
│ │ ├── user.module.ts
│ │ ├── user.resolver.spec.ts
│ │ ├── user.resolver.ts
│ │ ├── user.service.spec.ts
│ │ └── user.service.ts
Update connection error
Can't create connection, getConnection().isConnected = false and
getConnection('test') (or 'default') throw "Connection was not found." error.
//connection.ts
import { loadEnv } from '../loadEnv';
import connectionOptions from '../ormconfig-cli';
loadEnv();
async create(): Promise<Connection> {
const connOptions: ConnectionOptions = connectionOptions.find(option => option.name == process.env.NODE_ENV);
return await createConnection({ ...connOptions, name: 'default' });
},
Here's the setting:
// package.json
{
...,
"scripts": {
...,
"test": "NODE_ENV=test jest",
"test:watch": "jest --watch",
"test:cov": "jest --coverage",
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
"test:e2e": "jest --config ./test/jest-e2e.json",
}, {...},
"jest": {
"moduleFileExtensions": [
"js",
"json",
"ts"
],
"rootDir": "src",
"testRegex": ".*\\.spec\\.ts$",
"transform": {
"^.+\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": [
"**/*.(t|j)s"
],
"coverageDirectory": "../coverage",
"testEnvironment": "node",
"clearMocks": true
}
}
// .env.test
NODE_ENV=test
TYPEORM_HOST=localhost
TYPEORM_PORT=5432
TYPEORM_USERNAME=root
TYPEORM_PASSWORD=test
TYPEORM_DATABASE=test
# TEST_DATABASE="postgres://root:test@localhost:5432/test jest"
// loadEnv.ts
import * as dotenv from 'dotenv';
export const loadEnv = () => {
const loadFile = () => {
if (process.env.NODE_ENV === 'test') return '.env.test';
return '.env';
};
return dotenv.config({
path: loadFile(),
});
};
// ormconfig.ts
import { loadEnv } from './loadEnv';
import { ConnectionOptions } from 'typeorm';
loadEnv();
const SnakeNamingStrategy = require('typeorm-naming-strategies')
.SnakeNamingStrategy;
const connectionOptions: ConnectionOptions[] = [
{
name: 'development',
type: 'postgres',
host: String(process.env.TYPEORM_HOST),
port: Number(process.env.TYPEORM_PORT),
username: String(process.env.TYPEORM_USERNAME),
password: String(process.env.TYPEORM_PASSWORD),
database: String(process.env.TYPEORM_DATABASE),
synchronize: false,
logging: true,
cli: {
migrationsDir: 'src/migrations',
},
namingStrategy: new SnakeNamingStrategy(),
entities: ['dist/**/*.entity{.ts,.js}'],
migrations: ['dist/src/migrations/*.{js,ts}'],
migrationsTransactionMode: 'each',
},
{
name: 'test',
type: 'postgres',
host: String(process.env.TYPEORM_HOST),
port: Number(process.env.TYPEORM_PORT),
username: String(process.env.TYPEORM_USERNAME),
password: String(process.env.TYPEORM_PASSWORD),
database: String(process.env.TYPEORM_DATABASE),
synchronize: false,
dropSchema: true,
logging: true,
cli: {
migrationsDir: 'src/migrations',
},
namingStrategy: new SnakeNamingStrategy(),
entities: ['dist/**/*.entity{.ts,.js}'],
migrations: ['dist/src/migrations/*.{js,ts}'],
migrationsTransactionMode: 'each',
},
];
export = connectionOptions;
// app.module.ts
@Module({
imports: [
TypeOrmModule.forRoot({
name: 'development',
type: 'postgres',
host: process.env.TYPEORM_HOST,
username: process.env.TYPEORM_USERNAME,
password: process.env.TYPEORM_PASSWORD,
database: process.env.TYPEORM_DATABASE,
migrationsRun: true,
synchronize: false,
migrationsTransactionMode: 'each',
migrations: ['dist/src/migrations/*.{js,ts}'],
namingStrategy: new SnakeNamingStrategy(),
autoLoadEntities: true,
}),
TypeOrmModule.forRoot({
name: 'test',
type: 'postgres',
host: process.env.TYPEORM_HOST,
username: process.env.TYPEORM_USERNAME,
password: process.env.TYPEORM_PASSWORD,
database: process.env.TYPEORM_DATABASE,
logging: false,
dropSchema: true,
migrationsRun: true,
synchronize: false,
migrationsTransactionMode: 'each',
migrations: ['dist/src/migrations/*.{js,ts}'],
namingStrategy: new SnakeNamingStrategy(),
autoLoadEntities: true,
}),
...,
],
})
export class AppModule {}
// connection.ts
import { Connection, createConnection, getConnection, getConnectionOptions } from 'typeorm';
import { loadEnv } from '../loadEnv';
loadEnv();
export const connection = {
async create(): Promise<Connection> {
const connectionOptions = await getConnectionOptions(process.env.NODE_ENV);
return await createConnection({ ...connectionOptions, name: 'default' });
},
async close() {
await getConnection().close();
},
async clear() {
const connection = getConnection();
const entities = connection.entityMetadatas;
entities.forEach(async (entity) => {
const repo = connection.getRepository(entity.name);
await repo.query(`DELETE FROM ${entity.tableName}`);
});
},
async isConnected() {
return getConnection().isConnected;
},
};
If run npm test src/test/user.service.spec.ts, will get error: No connection options were found in any orm configuration files.
The log console.log("connection.isConnected(): ", connection.isConnected()); get error:
ConnectionNotFoundError: Connection "default" was not found.
Here's the test case:
import { getConnection, Repository } from 'typeorm';
import { createMock } from '@golevelup/nestjs-testing';
import { User } from '../user/user.entity';
import { connection } from '../connection';
describe('UserService', () => {
beforeAll(async () => {
await connection.create();
});
afterAll(async () => {
await connection.clear();
await connection.close();
});
describe('beforeAll connection.create()', async () => {
const repo = createMock<Repository<User>>();
it('should create user', function() {
console.log('env: ', process.env.NODE_ENV); // env: test
console.log("connection.isConnected(): ", connection.isConnected());
const user = {
id: 'uuid',
avatar: 'avatar',
name: 'user A',
createdAt: new Date(),
updatedAt: new Date(),
deletedAt: new Date(),
};
console.log(user);
repo.create(user);
repo.find.mockResolvedValue([user]);
});
});
});
If not using connection.create() to connect, connection.isConnected() will be true.
...
beforeAll(async () => {
await createConnection({
type: "postgres",
host: "localhost",
port: 5432,
username: "root",
password: "test",
database: "test",
logging: false,
});
});
...
How could I update my setting and connect database through my test config?