node.js - typeORM'un getCustomRepository'sini nasıl taklit ederim


Yapılandırıcısında getCustomRepository kullanan bir sınıfı birim testi yapmak istiyorum ama bunu kolayca nasıl taklit edeceğimi bir türlü bulamıyorum. İşte sınıfımın kodu

import {getCustomRepository} from 'typeorm';

export class Controller {
  private repository: UserRepository;

  constructor() {
    this.repository = getCustomRepository(UserRepository); //I want to mock this.
  }

  async init() {
    return this.repository.findUser(1);
  }
}

ve işte testler

describe('User Tests', () => {
  it('should return user', async () => {
    //Fake user to be resolved.
    const user = new User();
    user.id = 2;

    //I want to mock  getCustomRepository(UserRepository); here
    //getCustomRepository = jest.fn().mockResolvedValue(UserRepository); HERE HOW???

    //Mocking find user
    UserRepository.prototype.findUser = jest.fn().mockResolvedValue(user);

    const controller = new Controller();
    const result = await controller.init();
    expect(result).toBeDefined();
  });
});

Not: Depo metodlarını taklit etmek iyi çalışıyor ama aslında getCustomRepository'yi taklit etmek istiyorum çünkü bu, veritabanına bağlanmaya çalışırken harcanan zamanı azaltabilir.

TypeORM'deki getCustomRepository böyle görünüyor

export declare function getCustomRepository<T>(customRepository: ObjectType<T>, connectionName?: string): T;

KullanıcıDeposu.ts

@EntityRepository(User)
export class UserRepository extends Repository<User> {
  public async findUser(id: number) {
    return 'real user';
  }
}

Kullanıcı.ts

@Entity('users')
export class User{
  @PrimaryGeneratedColumn()
  id: number;

  @Column({type: 'varchar', length: 100})
  name: string;
}

Yani soru şu: Bunu nasıl taklit edebilirim? Her türlü yardım çok takdir edilecektir.

Cevap #1

Jest kullanarak typeorm modülünü, getCustomRepository fonksiyonunu ve döndürülen değerini taklit etmek için jest.mock(moduleName, factory, options) kullanabilirsiniz.

Örn.

controller.ts:

import { getCustomRepository } from 'typeorm';
import { UserRepository } from './userRepo';

export class Controller {
  private repository: UserRepository;

  constructor() {
    this.repository = getCustomRepository(UserRepository);
  }

  async init() {
    return this.repository.findUser(1);
  }
}

userRepo.ts:

export class UserRepository {
  public async findUser(id: number) {
    return 'real user';
  }
}

controller.test.ts:

import { Controller } from './controller';
import { getCustomRepository } from 'typeorm';
import { mocked } from 'ts-jest/utils';
import { UserRepository } from './userRepo';

jest.mock('typeorm', () => ({ getCustomRepository: jest.fn() }));

describe('61693597', () => {
  it('should pass', async () => {
    const userRepo = { findUser: jest.fn().mockResolvedValueOnce('fake user') };
    mocked(getCustomRepository).mockReturnValueOnce(userRepo);
    const controller = new Controller();
    const actual = await controller.init();
    expect(actual).toBe('fake user');
    expect(getCustomRepository).toBeCalledWith(UserRepository);
    expect(userRepo.findUser).toBeCalledWith(1);
  });
});

kapsam raporu ile birim testi sonuçları:

 PASS  stackoverflow/61693597/controller.test.ts (13.53s)
  61693597
    ✓ should pass (9ms)

---------------|---------|----------|---------|---------|-------------------
File           | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
---------------|---------|----------|---------|---------|-------------------
All files      |   92.31 |      100 |      80 |   90.91 |                   
 controller.ts |     100 |      100 |     100 |     100 |                   
 userRepo.ts   |      80 |      100 |      50 |      75 | 3                 
---------------|---------|----------|---------|---------|-------------------
Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        15.596s

kaynak kodu: https://github.com/mrdulin/react-apollo-graphql-starter-kit/tree/master/stackoverflow/61693597

Cevap #2

Daha önce bir yorumda bahsedildiği gibi, @slideshowp2'nin cevabını çoğaltmayı denedim. ts-jest'ten gelen mocked() yardımcı işlevi 'mockResolvedValueOnce' değerini tanımsız olarak döndürüyordu.

Bunu çözmek için, onu birkaç adıma ayırmak zorunda kaldım:

  1. daha derinlemesine taklit yapmak için bir bağımlılık olarak sinon ekleyin
  2. (çok önemli) getCustomRepository fonksiyonu için bir kapsayıcı oluştur

Sarmalayıcı olmadan, özelliğin salt okunur olması nedeniyle dönüş değerini taklit etmek neredeyse imkansızdır. Şu gibi bir şey yapmak yeterli olmalıdır:

c: Connection;

public async getCustomRepository<T>(type: ObjectType<T>): Promise<T> {
    if (!this.c || !this.c.isConnected) {
      this.c = await this.getConnection();
    }
    return await this.c.getCustomRepository(type);
  }

Bunu yaptıktan sonra, getCustomRepository fonksiyonunu şu şekilde taklit etmeyi başardım:

describe('Unit tests', () => {
let sandbox: sinon.SinonSandbox;
let useCase: FindNodeUseCase;
let repositoryStub: sinon.SinonStub;
let nodeTreeRepo: NodeTreeRepository;

beforeEach(() => {
  sandbox = sinon.createSandbox();
  useCase = new FindNodeUseCase();

  const mockNode = new NodeTree();
  mockNode.idNode = 1;
  mockNode.level = 1;
  nodeTreeRepo = new NodeTreeRepository()
  nodeTreeRepo.findOne = jest.fn().mockResolvedValue(mockNode)
  nodeTreeRepo.findByIdAndLanguage = jest.fn().mockReturnValue(
    [
      [{ idNode: 1, level: 2, names: [{ idNode: 1, nodeName:"Sales" }] }],
      1
    ]
  );
  repositoryStub = sandbox.stub(useCase.databaseManager, 'getCustomRepository').resolves(nodeTreeRepo)
})

afterEach(() => {
  sandbox.restore();
})

İlgili makaleler