question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Guide / Sample for Testing with Jest

See original GitHub issue

It would be great to have a guide to using jest and typeorm together.

This guide would ideally have patterns for both using real connections for integration testing as well as mocking the TypeORM connection / repository / etc entirely.

Currently, if we mock jest.mock TypeORM the entity definitions stop working as expected.


Original Issue Description Below


Issue type:

[ X ] question [ ] bug report [ X ] feature request [ ] documentation issue

Database system/driver:

[ ] cordova [ ] mongodb [ ] mssql [ ] mysql / mariadb [ ] oracle [ X] postgres [ ] cockroachdb [ ] sqlite [ ] sqljs [ ] react-native [ ] expo

TypeORM version:

[ X] latest [ ] @next [ ] 0.x.x (or put your version here)

Is there any standard way to test typeorm based model etc.? I don’t mind direct connection to database for integration testing (especially mocking gets too complex)

I setup global connection like this.

export async function setupConn(drop=false) {
  conn = await createConnection({
    type: "postgres",
    synchronize: drop,
    dropSchema: drop,
    logging: false,
    url: "postgres://quantum@localhost/test",
    entities: [__dirname + "/../../src/entities/**/*.js"],
  })
  return conn
}

Then I tried to use global setup/ different environment calling setupConn function but the problem is:

Jest spawns worker for each test suites (different *.test.js) file.

So I always see this error

{"data": null, "errors": [[GraphQLError: Connection "default" was not found.]]}

but if i use same function at beforeAll at test suites it works for 1 test suite .

So is there any good and recommend way to test application developed using typeorm.

And thanks for making such a beautiful orm I really enjoy using it 😃

Issue Analytics

  • State:open
  • Created 4 years ago
  • Reactions:25
  • Comments:28 (3 by maintainers)

github_iconTop GitHub Comments

21reactions
guiaramoscommented, Jul 3, 2020

Solution for unit test with TypeORM (edited with @sarfata recommendations, thanks!)

it’s working now, hope it helps you guys and please let me know if there is any improvement.

settings

package.json

{
  "scripts": {
    "test": "jest",
    "test:cache": "jest --clearCache",
    "test:update": "jest -u",
    "test:commit": "jest --bail —-findRelatedTests",
    "lint": "tsc --noEmit && eslint 'src/**/*.ts{x,}' --fix --quiet && prettier-check --ignore-path .gitignore \"{.,src/**}/{*.ts{x,},*.md,ts*.json,*.config.js}\"",
  },
  "dependencies": {
    "@drdgvhbh/postgres-error-codes": "0.0.6",
    "cors": "^2.8.5",
    "dotenv": "^8.2.0",
    "reflect-metadata": "^0.1.13",
    "tslib": "1.11.2",
    "typeorm": "^0.2.24",
  },
  "devDependencies": {
    "@types/cookie-parser": "^1.4.2",
    "@types/jest": "^25.2.3",
    "@types/node": "^13.9.0",
    "@typescript-eslint/eslint-plugin": "^2.23.0",
    "@typescript-eslint/parser": "^2.23.0",
    "eslint": "^7.3.1",
    "eslint-config-airbnb-typescript": "^7.0.0",
    "eslint-config-prettier": "^6.10.0",
    "eslint-import-resolver-typescript": "^2.0.0",
    "eslint-plugin-import": "^2.20.2",
    "eslint-plugin-jest": "^23.17.1",
    "eslint-plugin-prettier": "^3.1.3",
    "husky": "^4.2.5",
    "jest": "^26.1.0",
    "lint-staged": "^10.2.2",
    "prettier": "^2.0.5",
    "prettier-check": "^2.0.0",
    "pretty-quick": "^2.0.1",
    "ts-jest": "^26.1.1",
    "ts-node-dev": "^1.0.0-pre.44",
    "typescript": "^3.9.5",

  },
  "lint-staged": {
    "**/*.ts{x,}": [
      "npm run lint",
      "npm run test:commit"
    ]
  }
}

ormconfig.ts

import { ConnectionOptions } from 'typeorm';
import { loadEnv } from './src/libraries/loadEnv';

loadEnv();

const DATABASE_TYPE = 'postgres';
const DATABASE_ENTITIES = ['src/entities/**/**.postgres.ts'];

const connectionOptions: ConnectionOptions[] = [
  {
    name: 'default',
    type: DATABASE_TYPE,
    database: String(process.env.DATABASE_DATABASE),
    host: String(process.env.DATABASE_HOST),
    port: Number(process.env.DATABASE_PORT),
    username: String(process.env.DATABASE_USERNAME),
    password: String(process.env.DATABASE_PASSWORD),
    entities: DATABASE_ENTITIES,
    synchronize: true,
    // dropSchema: true,
    logging: true
  },
  {
    name: 'test',
    type: DATABASE_TYPE,
    database: String(process.env.DATABASE_DATABASE),
    host: String(process.env.DATABASE_HOST),
    port: Number(process.env.DATABASE_PORT),
    username: String(process.env.DATABASE_USERNAME),
    password: String(process.env.DATABASE_PASSWORD),
    entities: DATABASE_ENTITIES,
    synchronize: true,
    dropSchema: true,
    logging: false
  }
];

export = connectionOptions;
];

export = connectionOptions;

src/libraries/loadEnv.ts

import { isProd, isTest } from 'src/constants/Envoriment';
import * as dotenv from 'dotenv';

export const loadEnv = () => {
  const loadFile = () => {
    if (isProd) return '.env';
    if (isTest) return '.env.test';
    return '.env.dev';
  };

  return dotenv.config({ path: loadFile() });
};

jest.config.js

const { pathsToModuleNameMapper } = require('ts-jest/utils');
const { compilerOptions } = require('./tsconfig.json');

module.exports = {
  preset: 'ts-jest',
  testEnvironment: 'node',
  clearMocks: true,
  maxWorkers: 1,
  moduleNameMapper: pathsToModuleNameMapper(compilerOptions.paths, { prefix: '<rootDir>/' }),
  testMatch: ['**/?(*.)+(spec|test).[jt]s?(x)'],
  setupFilesAfterEnv: ['<rootDir>/src/test-utils/db-env.ts']
};

src/test-utils/db-env.ts

// This file is executed once in the worker before executing each test file. We
// wait for the database connection and make sure to close it afterwards.
import setupServer from 'src/server/server.factory';

process.env.NODE_ENV = 'test';

beforeAll(async () => {
  const t0 = Date.now();
  const connection = await setupServer.connectionPostgres.create('test');
  const connectTime = Date.now();
  await connection.runMigrations();
  const migrationTime = Date.now();
  console.log(
    ` 👩‍🔬 Connected in ${connectTime - t0}ms - Executed migrations in ${
      migrationTime - connectTime
    }ms.`
  );
});
afterAll(async () => {
  await setupServer.connectionPostgres.close();
});

server.ts

 connectionPostgres: {
    async create(connectionName: 'default' | 'test' = 'default'): Promise<Connection> {
      const connectionOptions = await getConnectionOptions(connectionName);
      const connection = await createConnection({ ...connectionOptions, name: 'default' });
      return connection;
    },

    async close(): Promise<void> {
      await getConnection().close();
    },

    async clear(): Promise<void> {
      const connection = getConnection();
      const entities = connection.entityMetadatas;

      await Promise.all(
        entities.map(async (entity) => {
          const repository = connection.getRepository(entity.name);
          await repository.query(`DELETE FROM ${entity.tableName}`);
        })
      );
    }
  },

Test

src/entities/Postgres/User/tests/User.test.ts

import setupServer from 'src/server/server.factory';
import User from '../User.postgres';

describe('User entity', function () {
  beforeAll(async () => {
    await setupServer.connectionPostgres.create();
  });
  afterAll(async () => {
    await setupServer.connectionPostgres.close();
  });

  it('should be empty', async function () {
    const count = await User.count();
    expect(count).toBe(0);
  });
});

Docker (optional - for development only, never use docker for db)

docker-compose.db.yaml

version: '3.3'

services:
  postgres:
    image: postgres:11
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: password
      POSTGRES_DB: db
    ports:
      - 5432:5432

  postgres_tests:
    image: postgres:11
    environment:
      POSTGRES_USER: test
      POSTGRES_PASSWORD: test
      POSTGRES_DB: test
    ports:
      - 5433:5433
    command: -p 5433
14reactions
sarfatacommented, Jul 3, 2020

@guiaramos I believe with your solution each test file will redrop and recreate the database completely. This might take a bit of time.

Instead, you can define a globalSetup to run the migrations and connect (without executing migrations) in each of your tests.

This works for me and took me a very long time to figure out. I 👍 this issue, there should be clearer documentation on how to do this.

jest.config

  globalSetup: "./src/test-utils/setup-db.ts",
  setupFilesAfterEnv: ["./src/test-utils/db-env.ts"],

setup-db.ts

// Those first two require are very important - without them the typescript migrations did not work for me.
// See https://github.com/facebook/jest/issues/10178

// tslint:disable-next-line: no-var-requires
require("ts-node/register")
// tslint:disable-next-line: no-var-requires
require("tsconfig-paths/register")
import "dotenv/config"
import { createConnection } from "typeorm"
import { PostgresConnectionOptions } from "typeorm/driver/postgres/PostgresConnectionOptions"
import ormConfig from "../ormconfig"

/*
 * This file is executed by Jest before running any tests.
 * We drop the database and re-create it from migrations every time.
 */
export default async () => {
  // Force dropping the schema so that test run clean every time.
  // Note that we are not cleaning *between* tests.
  const testOrmConfig: PostgresConnectionOptions = {
    ...(ormConfig as PostgresConnectionOptions),
    dropSchema: true,
  }

  const t0 = Date.now()
  const connection = await createConnection(testOrmConfig)
  const connectTime = Date.now()
  await connection.runMigrations()
  const migrationTime = Date.now()
  console.log(
    ` 👩‍🔬 Connected in ${connectTime -
      t0}ms - Executed migrations in ${migrationTime - connectTime}ms.`
  )
}

db-env.ts

// This file is executed once in the worker before executing each test file. We
// wait for the database connection and make sure to close it afterwards.

import { getConnection } from "typeorm"
import { createDatabaseConnection } from "~/repositories/DBConnection"

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

And my createDatabaseConnection is just:

import { createConnection } from "typeorm"
import ormConfig from "../ormconfig"

export async function createDatabaseConnection() {
  return await createConnection(ormConfig)
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

A Beginner's Guide to Unit-testing with Jest
For Javascript, Jest is one of the most widely used testing frameworks, and I hope this blog serves as a beginner's guide for...
Read more >
Jest Tutorial - JavaScript Unit Testing Using Jest Framework
In this Jest tutorial, we will learn about various Jest features, Jest matchers and how to use Jest framework for JavaScript Unit Testing....
Read more >
Getting Started - Jest
Let's get started by writing a test for a hypothetical function that adds two numbers. First, create a sum.js file: function sum(a, b)...
Read more >
Jest Tutorial for Beginners: Getting Started With JavaScript ...
The guide covers unit testing components, class components, functional components with hooks, and the new Act API.
Read more >
Jest Testing Tutorial: 5 Easy Steps - Testim Blog
1. Install Jest Globally · 2. Create a Sample Project · 3. Add Jest to the Project · 4. Write Your First Test...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found