Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to mock typeorm connection using jest #5751

Closed
teknolojia opened this issue Mar 24, 2020 · 12 comments
Closed

How to mock typeorm connection using jest #5751

teknolojia opened this issue Mar 24, 2020 · 12 comments

Comments

@teknolojia
Copy link

Issue type:

[ x] question

Database system/driver:

[ x] mssql

TypeORM version:

[ ] latest
[ ] @next
[x ] 0.x.x (0.2.22)

Steps to reproduce or a small repository showing the problem:

In integration tests I am using the following snippets to create connection

import {Connection, createConnection} from 'typeorm';
// @ts-ignore
import options from './../../../ormconfig.js';

export function connectDb() {
  let con: Connection;

  beforeAll(async () => {
    con = await createConnection(options);
  });

  afterAll(async () => {
    await con.close();
  });

}

I am trying to unit test a class which calls typeorm repository in one of its method and without call that helper function connectDb() above I get the following error which is expected of course.

ConnectionNotFoundError: Connection "default" was not found.

My question is how can I mock connection. I have tried the following without any success

import typeorm, {createConnection} from 'typeorm';
// @ts-ignore
import options from "./../../../ormconfig.js";

const mockedTypeorm = typeorm as jest.Mocked<typeof typeorm>;

jest.mock('typeorm');

 beforeEach(() => {
    //mockedTypeorm.createConnection.mockImplementation(() => createConnection(options)); //Failed
    mockedTypeorm.createConnection = jest.fn().mockImplementation(() => typeorm.Connection);

    MethodRepository.prototype.changeMethod = jest.fn().mockImplementation(() => {
      return true;
    });
  });

Running tests with that kind of mocking gives this error

TypeError: decorator is not a function

Note: if I call connectDb() in tests everything works fine. But I don't want to do that since it takes too much time as some data are inserted into db before running any test. Some codes have been omitted for simplicity. Any help will be appreciated

@teknolojia teknolojia changed the title How to mock typeorm connection How to mock typeorm connection using jest Mar 24, 2020
@jparr721
Copy link

jparr721 commented Jun 17, 2020

Recently experiencing this issue, would love to know if there is a solution. The only workaround I know is to do the following:

import typeorm = require('typeorm');

// in the test
typeorm.typeormFunction = jest.fn().(dosomething)

@imnotjames
Copy link
Contributor

Closing as a duplicate of #5308

@sgentile
Copy link

5308 does not cover mocking a typeorm connection with Jest

@artyom-88
Copy link

artyom-88 commented Jun 21, 2021

It's not a duplicate. #5308 requires real DB (or container) to tun tests.
The goal of current issue is to mock 'typeorm' and run tests without real DB connection.
@imnotjames could you please, reopen the issue?

@imnotjames
Copy link
Contributor

imnotjames commented Jun 21, 2021

The linked duplicate is requesting a guide to using jest as part of your testing.

This is requesting a guide to using test as part of your testing.

The different is that the linked issue only describes one kind of testing.

I've updated the linked issue to note that documentation should include patterns for mocking as well.

Given how incredibly similar these are from an implementation standpoint I'll be leaving this closed unless I'm really misunderstanding the request here.

Cheers.

@sparkts-shaun
Copy link

I'm in agreement with @Artyom-Ganev, as I am also getting the same error TypeError: decorator is not a function @teknolojia mentioned.

In attempting to mock typeorm for tests without a db connection there is some weird interplay between nest and typeorm that I think goes beyond simply a general guide to usage. At the very least, if we could come up with a resolution to that error it would be helpful.

@sgentile
Copy link

sgentile commented Dec 7, 2021 via email

@sparkts-shaun
Copy link

@sgentile did you have the decorator is not a function issue as well? I've found some things on SO about that, but haven't been able to eliminate it with mocks.

@Josega149
Copy link

Josega149 commented Feb 23, 2022

I just upgrade from 0.2.21 to 0.2.43 and all my tests crashed. I used to do:

let transactionEntityManager: EntityManager;

typeorm.getManager = jest.fn().mockReturnValue({
      transaction: jest.fn().mockImplementation((cb) => cb(transactionEntityManager)),
});

But now the mock is not working and I get a "Connection "default" was not found.". I have more than 300 unit test. Anyone solved this?

@andreea96
Copy link

andreea96 commented Mar 3, 2022

Had the same issue with getCustomRepository method, manage to work around it by mocking the method from the 'typeorm/globals' folder instead of 'typeorm'(index folder)

import * as typeorm_functions from 'typeorm/globals';
jest.spyOn(typeorm_functions, 'getCustomRepository').mockReturnValue(mockRepo);

i would assume there is the same issue with getManager and createConnection methods since they are in the same globals file as the getCustomRepository method.

@Alex100dre
Copy link

Had the same issue with getCustomRepository method, manage to work around it by mocking the method from the 'typeorm/globals' folder instead of 'typeorm'(index folder)

import * as typeorm_functions from 'typeorm/globals';
jest.spyOn(typeorm_functions, 'getCustomRepository').mockReturnValue(mockRepo);

i would assume there is the same issue with getManager and createConnection methods since they are in the same globals file as the getCustomRepository method.

This worked for me with getManager function

import * as typeorm_functions from 'typeorm/globals';

const queryMock = jest.fn().mockReturnValue(new Promise<Service[]>(resolve => resolve(queryRes);

jest.spyOn(typeorm_functions, 'getManager').mockReturnValue({
    query: queryMock;
});

@bernard-blackpoint
Copy link

bernard-blackpoint commented Feb 2, 2023

DISCLAIMER: I haven't gotten into any actual operations against the database - which maybe doesn't matter since I imagine those would need to be mocked per test case anyway - but FWIW:

Here's a minimal mock that will at least support creation / merging of entities:

Tested against typeorm@0.2.45 ONLY

UPDATE: Tested against both typeorm@^0.2 and typeorm@^0.3

Source

setup.ts
import type * as typeorm from 'typeorm';
import type { PostgresDriver } from 'typeorm/driver/postgres/PostgresDriver';

jest.mock('typeorm/connection/Connection', () => {
	const module: typeof import('typeorm/connection/Connection') = jest.requireActual(
		'typeorm/connection/Connection',
	);

	const { RelationLoader } = jest.requireActual(
		'typeorm/query-builder/RelationLoader',
	);

	const { DefaultNamingStrategy } = jest.requireActual(
		'typeorm/naming-strategy/DefaultNamingStrategy',
	);

	const { DriverFactory } = jest.requireActual('typeorm/driver/DriverFactory');

	/**
	 * @this typeorm.Connection
	 */
	function Connection(
		this: typeorm.Connection,
		options: typeorm.ConnectionOptions,
	): void {
		Object.assign(this, {
			driver: undefined,
			entityMetadatas: [],
			isConnected: false,
			logger: {},
			manager: undefined,
			migrations: [],
			name: options.name || 'default',
			namingStrategy: options.namingStrategy || new DefaultNamingStrategy(),
			options,
			queryResultCache: undefined,
			relationLoader: undefined,
			subscribers: [],
		});

		Reflect.set(this, 'driver', new DriverFactory().create(this));
		Reflect.set(this, 'manager', this.createEntityManager());
		Reflect.set(this, 'relationLoader', new RelationLoader(this));
	}

	Connection.prototype = module.Connection.prototype;

	Connection.prototype.close = async function close(): Promise<void> {
		Reflect.set(this, 'isConnected', false);
	};

	Connection.prototype.connect = async function connect(): Promise<typeorm.Connection> {
		Reflect.set(this, 'isConnected', true);
		await Reflect.get(this, 'buildMetadatas').call(this);
		return this;
	};

	Reflect.set(module, 'Connection', Connection);
	return module;
});

jest.mock('typeorm/driver/postgres/PostgresDriver', () => {
	const module: typeof import('typeorm/driver/postgres/PostgresDriver') = jest.requireActual(
		'typeorm/driver/postgres/PostgresDriver',
	);
	Reflect.set(module.PostgresDriver.prototype, 'loadDependencies', () => {});
	Reflect.set(module.PostgresDriver.prototype, 'connect', async () => {});
	Reflect.set(
		module.PostgresDriver.prototype,
		'afterConnect',
		/**
		 * @this PostgresDriver
		 */
		async function afterConnect(this: PostgresDriver) {
			this.version = '14.0';
			this.isGeneratedColumnsSupported = true;
		},
	);
	return module;
});
fixtures/parent.entity.ts
import { BaseEntity, Column, Entity, OneToOne, PrimaryColumn } from 'typeorm';

// eslint-disable-next-line import/no-cycle
import { Child } from './child.entity';

@Entity('parent')
export class Parent extends BaseEntity {
	@PrimaryColumn({ type: 'uuid' })
	public id: string;

	@Column()
	public name: string;

	@OneToOne(() => Child, child => child.parent)
	public child?: Child;
}
fixtures/child.entity.ts
import {
	BaseEntity,
	Column,
	Entity,
	JoinColumn,
	ManyToOne,
	PrimaryColumn,
} from 'typeorm';

// eslint-disable-next-line import/no-cycle
import { Parent } from './parent.entity';

@Entity('child')
export class Child extends BaseEntity {
	@PrimaryColumn({ type: 'uuid' })
	public id: string;

	@Column()
	public name: string;

	@Column({ type: 'uuid' })
	public parentId: string;

	@ManyToOne(() => Parent)
	@JoinColumn({ name: 'parentId' })
	public parent?: Parent;
}
entity.spec.ts
import faker from 'faker';
import { BaseEntity, createConnection } from 'typeorm';

import { Child, Parent } from './fixtures';

beforeAll(() =>
	createConnection({
		entities: [Child, Parent],
		name: 'default',
		schema: 'public',
		type: 'postgres',
	}),
);

describe('create', () => {
	it('should work', () => {
		const parent = Parent.create({
			child: {
				id: faker.datatype.uuid(),
				name: 'child',
			},
			id: faker.datatype.uuid(),
			name: 'parent',
		});
		expect(parent).toBeInstanceOf(BaseEntity);
		expect(parent.child).toBeInstanceOf(BaseEntity);
	});
});

describe('merge', () => {
	it('should work', async () => {
		const parent = Parent.create({
			child: {
				id: faker.datatype.uuid(),
				name: 'child',
			},
			id: faker.datatype.uuid(),
			name: 'parent',
		});
		const patch = { child: { name: 'child renamed' } };
		Parent.merge(parent, patch);
		expect(parent).toMatchObject(patch);
	});
});

Result

$ npx jest entity.spec
PASS  entity.spec.ts
 create
   ✓ should work (2 ms)
 merge
   ✓ should work (1 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        4.697 s
Ran all test suites matching /entity.spec/i.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

10 participants