Jason Van Malder
Jason Van Malder

Reputation: 617

Jest/TypeORM purge database after all tests

I would like to delete all entries into my database after or before all jest tests.

Here is my setup.js:

import { getConnection, getConnectionManager } from "typeorm"

beforeAll(async () => {
    const connectionManager = getConnectionManager()
    const connection = connectionManager.create({
        "type": "postgres",
        "host": "localhost",
        "port": 54320,
        "username": "test",
        "password": "test",
        "database": "test",
        "entities": ["../src/entities/*.ts"],
        "logging": false,
        "synchronize": true
    })
    await connection.connect()
})

afterAll(async () => {
    await getConnection().close()
})

I read on the typeorm documentation that the "synchronize" option would override the old tables with new one which are empty but it doesn't seems to work.

Here is the test I made:

describe('POST /create', () => {
    it('Create a user', async () => {
        const user: IStringTMap<string> = {
            firstName: 'John',
            lastName: 'Doe',
            email: '[email protected]',
            password: 'test123!',
        }

        const res: Response = await request(app)
            .post('/create')
            .send(user)
            .expect(200)

        expect(res.type).toEqual('application/json')
        expect(res.body.email).toBe('[email protected]')
        expect(res.body.password).toBe(undefined)
    })
})

First yarn test works but the next one doesn't (email already exists)

Any idea?

Upvotes: 16

Views: 27548

Answers (6)

Yauhen
Yauhen

Reputation: 469

Note: examples with getConnection is deprecated now. A new approach could be:

import { DataSource } from 'typeorm';
import { databaseConfig } from '../src/config/database'; // TypeORM configuration

export const clearDatabase = async () => {
  const appDataSource = new DataSource(databaseConfig);
  const entities = appDataSource.entityMetadatas;

  for await (const entity of entities) {
    const repository = appDataSource.getRepository(entity.name);

    await repository.query(
      `TRUNCATE ${entity.tableName} RESTART IDENTITY CASCADE;`,
    );
  }
};

Upvotes: 2

yolan PIBRAC
yolan PIBRAC

Reputation: 191

Here is a simple and efficient way to fully clean a DB with typeorm, in creating a dedicated TestService which TRUNCATE all entities in one command:

import { Inject, Injectable } from "@nestjs/common";
import { Connection } from "typeorm";

@Injectable()
export class TestService {
  constructor(@Inject("Connection") public connection: Connection) {}

  public async cleanDatabase(): Promise<void> {
    try {
      const entities = this.connection.entityMetadatas;
      const tableNames = entities.map((entity) => `"${entity.tableName}"`).join(", ");
      
      await this.connection.query(`TRUNCATE ${tableNames} CASCADE;`);
      console.log("[TEST DATABASE]: Clean");
    } catch (error) {
      throw new Error(`ERROR: Cleaning test database: ${error}`);
    }
  }
}

Then you can call this function in your testing files:

beforeEach(async () => {
  await testService.cleanDatabase();
});

Upvotes: 7

Diaboloxx
Diaboloxx

Reputation: 311

Sorry I do not have enough reputation to respond to Michiel's comment but,

if you are using a postgreSQL database and your table have foreign key constraints your code may not work because according to the postgreSQL documentation TRUNCATE does not fire an ON DELETE trigger

The TRUNCATE TABLE does not fire ON DELETE trigger. Instead, it fires the BEFORE TRUNCATE and AFTER TRUNCATE triggers.

So you will probably have to use the EntityManager to make an SQL query like this:

entityManager.query('TRUNCATE TABLE table_name CASCADE;')

Hope you find this helpful.

Upvotes: 5

Michiel
Michiel

Reputation: 1843

Maybe a little late but was searching for this as well, this is what I came up with.

This will only delete the content inside the entities, not the entities itself.

afterEach(async () => {

    // Fetch all the entities
    const entities = getConnection().entityMetadatas;

    for (const entity of entities) {
        const repository = getConnection().getRepository(entity.name); // Get repository
        await repository.clear(); // Clear each entity table's content
    }
});

EDIT: If you're using foreign keys, make sure to add the {onDelete: "CASCADE"} property to your columns in order to delete all the records correctly.

More information about that can be found here: https://github.com/typeorm/typeorm/issues/1460#issuecomment-366982161

Upvotes: 20

fafa.mnzm
fafa.mnzm

Reputation: 611

you can setup a new database in your e2e test file with a separate name and set the dropSchema: true option when you import the typeorm module. you can import the typeorm module int he imports the same way you do in AppModule

Upvotes: 3

Isaac Pak
Isaac Pak

Reputation: 4931

You may not have to purge the database for this test. You could alternatively use a library like faker and bypass the email duplicate validation. Faker will produce a unique user with every test run. Change your user to:


import fake from 'faker';

const user: IStringTMap<string> = {
  firstName: faker.name.firstName(),
  lastName: faker.name.lastName(),
  email: faker.internet.email(),
  password: faker.internet.password()
}

Upvotes: -4

Related Questions