I am developing an application with NestJS and typeorm, but I have doubts like I populate it with test data. In my better try, first have generated a migration that create the database (with typeorm migration:generate) and after that in the second migration I try to populate the database:
public async up(queryRunner: QueryRunner): Promise<void> {
await getConnection().transaction(async transactionalEntityManager => {
const queryBuilder = await getConnection().createQueryBuilder();
let res3 = await queryBuilder.insert()
.into(Location)
.values({
name: "location X"
})
.execute();
// rest of inserts follow same fashion...
But it generates an error complaining as if the table Location does not exists. If I run a migration with (typeorm migration:run) only with my first migration on the migrations dir and after that I place my second migration the migration runs successfully. Though, if I run with the both migration scripts the aforementioned error is triggered. Does someone get to help me, at least with some idea to solve the problem, or a different way to work around the problem?
I wouldn't do database seeding in migrations. Especially since as you make schema changes over time you'll have to go back and update your migrations which is not ideal.
If you want test data I'd keep it separate from the migrations. For our tests we load test data from json files for each table before running jest. You could change it to load a sql dump or run inserts instead of json if you prefer.
Here is our TestUtils if you like that idea:
import { Injectable } from '@nestjs/common';
import { Connection, Repository } from 'typeorm';
import examplefrom './fixtures/example.json';
const fixtures = {
example,
...
};
/**
* This class is used to support database
* tests with unit tests in NestJS.
*
* This class is inspired by https://github.com/jgordor
* https://github.com/nestjs/nest/issues/409#issuecomment-364639051
*/
@Injectable()
export class TestUtils {
/**
* Creates an instance of TestUtils
*/
constructor(private readonly connection: Connection) {
if (process.env.ENVIRONMENT !== 'test') {
throw new Error(`ERROR-TEST-UTILS-ONLY-FOR-TESTS environment:${process.env.ENVIRONMENT}`);
}
}
async getRepository<T>(entity: any): Promise<Repository<T>> {
return this.connection.getRepository(entity);
}
/**
* Cleans the database and reloads the entries
*/
async reloadFixtures() {
await this.cleanAll();
await this.loadAll();
}
async wait(ms: number) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
/**
* Cleans all the entities
*/
async cleanAll() {
const entities = this.connection.entityMetadatas;
const query_runner = this.connection.createQueryRunner();
try {
for (const entity of entities) {
const repository = await this.getRepository(entity.name);
await query_runner.query(`ALTER TABLE ${repository.metadata.schema || 'public'}."${entity.tableName}" DISABLE TRIGGER ALL;`);
await query_runner.query(`DELETE FROM ${repository.metadata.schema || 'public'}."${entity.tableName}";`);
await query_runner.query(`ALTER TABLE ${repository.metadata.schema || 'public'}."${entity.tableName}" ENABLE TRIGGER ALL;`);
}
} catch (error) {
throw new Error(`ERROR: Cleaning test db: ${error}`);
}
await query_runner.release();
}
/**
* Insert the data from the src/test/fixtures folder
*/
async loadAll() {
const entities = this.connection.entityMetadatas;
const query_runner = this.connection.createQueryRunner();
try {
const loaded_tables: any = {};
for (const entity of entities) {
if (loaded_tables[entity.tableName]) continue;
loaded_tables[entity.tableName] = true;
const repository = await this.getRepository(entity.name);
// @ts-ignore
const items = fixtures[entity.tableName];
if (items) {
await query_runner.query(`ALTER TABLE ${repository.metadata.schema || 'public'}."${entity.tableName}" DISABLE TRIGGER ALL;`);
if (entity.discriminatorColumn) {
const child_entities = new Set(entity.childEntityMetadatas.map((c) => c.name));
const item_map: Dictionary<any> = {};
for (const item of items) {
const discriminator_column = item[entity.discriminatorColumn.propertyName];
if (!child_entities.has(discriminator_column)) {
throw new Error(`Invalid fixture. ${discriminator_column} not in ${[...child_entities].join(' ')}`);
}
if (!item_map[discriminator_column]) {
item_map[discriminator_column] = [];
}
item_map[discriminator_column].push(item);
}
for (const [child_entity, child_items] of Object.entries(item_map)) {
await query_runner.manager.insert(child_entity, child_items);
}
} else {
await query_runner.manager.insert(entity.name, items);
}
await query_runner.query(`ALTER TABLE ${repository.metadata.schema || 'public'}."${entity.tableName}" ENABLE TRIGGER ALL;`);
}
}
} catch (error) {
throw new Error(`ERROR [TestUtils.loadAll()]: Loading fixtures on test db: ${error}`);
}
await query_runner.release();
}
}