λ³Έ 글은 μ œκ°€ NestJS ν”„λ ˆμž„μ›Œν¬λ₯Ό 톡해 κ°œλ°œν•˜λ©΄μ„œ 깨달은 λ…Έν•˜μš°λ₯Ό κΈ°λ‘ν•œ κ²ƒμž…λ‹ˆλ‹€. μ œκ°€ μ œμ‹œν•œ 방법보닀 더 쒋은 방법이 μžˆμ„ μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. 지적은 μ–Έμ œλ‚˜ ν™˜μ˜μž…λ‹ˆλ‹€ :)

11 minute read

λ³Έ 글은 μ œκ°€ NestJS ν”„λ ˆμž„μ›Œν¬λ₯Ό 톡해 κ°œλ°œν•˜λ©΄μ„œ 깨달은 λ…Έν•˜μš°λ₯Ό κΈ°λ‘ν•œ κ²ƒμž…λ‹ˆλ‹€. μ œκ°€ μ œμ‹œν•œ 방법보닀 더 쒋은 방법이 μžˆμ„ μˆ˜λ„ μžˆμŠ΅λ‹ˆλ‹€. 지적은 μ–Έμ œλ‚˜ ν™˜μ˜μž…λ‹ˆλ‹€ :)




NestJS에선 Javascript의 ν…ŒμŠ€νŠΈ ν”„λ ˆμž„μ›Œν¬μΈ jestlinkλ₯Ό 기본으둜 ν•˜λŠ” ν…ŒμŠ€νŠΈ ν”„λ ˆμž„μ›Œν¬λ₯Ό μ§€μ›ν•œλ‹€.

λ¬Όλ‘  μ•½κ°„μ˜ λ³€ν˜•μ€ μžˆκ² μ§€λ§Œ, κ·Έλƒ₯ μ§€μ›λ§Œ ν•˜λŠ” μˆ˜μ€€μ΄ μ•„λ‹ˆλΌ NestJS의 ν…ŒμŠ€νŠΈλ₯Ό jest둜 ν•œλ‹€.

npm i --save-dev @nestjs/testing


ν…ŒμŠ€νŒ… 기초 μ½”λ“œ

NestJS CLIλ₯Ό μ΄μš©ν•΄ NestJS Objectλ₯Ό μƒμ„±ν•˜κ²Œ 되면 μžλ™μœΌλ‘œ ν…ŒμŠ€νŒ… 파일인 .spec.tsκ°€ μƒμ„±λœλ‹€.

이 .sepc.ts νŒŒμΌμ€ Controller와 serviceλ₯Ό NestJS CLI둜 생성할 λ•Œμ—λ§Œ μžλ™μœΌλ‘œ μƒμ„±λœλ‹€.

사싀 λ‘˜μ˜ μ°¨μ΄λŠ” 거의 μ—†λŠ”λ°, λ§Œμ•½ 차이λ₯Ό 보고 μ‹Άλ‹€λ©΄ νŽΌμ³λ³΄κΈ°μ— κΈ°μˆ μ€ 해두겠닀.

controller.spec.ts vs. serivice.spec.ts


  • app.controller.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { CatController } from './cat.controller';
import { CatService } from './cat.service';

describe('CatController', () => {
  let controller: CatController;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [CatController],
      service: [CatService]
    }).compile();

    controller = module.get<CatController>(CatController);
  });

  it('should be defined', () => {
    expect(controller).toBeDefined();
  });
});
  • app.service.spec.ts
import { Test, TestingModule } from '@nestjs/testing';
import { CatService } from './cat.service';

describe('CatService', () => {
  let service: CatService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [CatService],
    }).compile();

    service = module.get<CatService>(CatService);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });
});

정말 차이가 μ—†μ§€ μ•Šμ€κ°€? πŸ™‚


사싀 λ‘˜μ˜ 차이가 거의 μ—†κ³ , λ‹€λ₯Έ 포슀트λ₯Ό 찾아봐도 보톡 Controllerλ₯Ό κΈ°μ€€μœΌλ‘œ μž‘μ„±λ˜μ–΄ μžˆμ–΄μ„œ μ—¬κΈ°μ—μ„œλ„ controllerλ₯Ό κΈ°μ€€μœΌλ‘œ controller.spec.tsλ₯Ό μž‘μ„±ν•΄λ³΄κ² λ‹€.

NestJS App을 생성할 λ•Œ μžλ™μœΌλ‘œ μƒμ„±λ˜λŠ” app.controller.spec.ts νŒŒμΌμ΄λ‹€. μš°μ„  이 녀석을 도해(εœ–θ§£)ν•΄λ³΄μž.

import { Test, TestingModule } from '@nestjs/testing';
import { AppController } from './app.controller';
import { AppService } from './app.service';

describe('AppController', () => {
  let appController: AppController;

  beforeEach(async () => {
    const app: TestingModule = await Test.createTestingModule({
      controllers: [AppController],
      providers: [AppService],
    }).compile();

    appController = app.get<AppController>(AppController);
  });

  describe('root', () => {
    it('should return "Hello World!"', () => {
      expect(appController.getHello()).toBe('Hello World!');
    });
  });
});

jestjsλŠ” ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€λ₯Ό describe 내뢀에 μ •μ˜ν•œλ‹€.

μœ„μ˜ μ½”λ“œμ—μ„œλŠ” describe('AppController', ...)와 내뢀에 describe('root', ...)κ°€ μ •μ˜λ˜μ–΄ μžˆλ‹€.

describe()의 인자둜 λ“€μ–΄κ°€λŠ” String은 λ‹¨μˆœνžˆ Testing을 μ •μ˜ν•˜λŠ” 이름에 λΆˆκ³Όν•˜λ‹€. ν…ŒμŠ€νŠΈ λ‘œμ§μ—λŠ” 아무 관계가 μ—†λ‹€.

beforeEach()μ—λŠ” 각 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€ μ‹€ν–‰ 이전에 μ„ ν–‰ν•  λ‚΄μš©μ΄ μ •μ˜λ˜μ–΄ μžˆλ‹€.

μœ„μ˜ μ½”λ“œλŠ” TestingModule인 app을 μƒμ„±ν•œλ‹€.


μ‹€μ œ ν…ŒμŠ€νŒ… μ½”λ“œλŠ” it()μ—μ„œ μ •μ˜λœλ‹€. it()에도 String μΈμžκ°€ λ“€μ–΄κ°€λŠ”λ°, describe()의 그것이 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€μ„ μ •μ˜ν•˜λŠ” 이름이라면, it()의 String은 ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€μ— λŒ€ν•œ μ„€λͺ…을 μ˜λ―Έν•œλ‹€.


κ·Έλž˜μ„œ μš”μ•½ν•˜λ©΄ μ•„λž˜μ™€ κ°™λ‹€!

describe('test title', () => {
  it('test description', () => {
    expect("value-wanting-to-test").tobe("value-wanting-to-get")
  }
})



λ°μ΄ν„°λ² μ΄μŠ€ ν…ŒμŠ€νŠΈ

Jest 라이브러리λ₯Ό μ΄μš©ν•΄ λ°μ΄ν„°λ² μ΄μŠ€ ν…ŒμŠ€νŠΈλ₯Ό ν•  수 μžˆλŠ” 두 κ°€μ§€ 방법을 μ•Œμ•„λ³΄μž!

MockRepository

μ•žμ˜ 상황은 λ¬Έμžμ—΄ 비ꡐ μˆ˜μ€€μ˜ κ°„λ‹¨ν•œ ν…ŒμŠ€νŒ…μ΄μ§€λ§Œ, μ‹€μ œ μ„œλ²„λ₯Ό ν…ŒμŠ€νŒ…ν•˜κΈ° μœ„ν•΄μ„  λ°μ΄ν„°λ² μ΄μŠ€μ— μ ‘κ·Όν•˜λŠ” API듀을 ν…ŒμŠ€νŠΈν•΄μ•Ό ν•œλ‹€!

ν•˜μ§€λ§Œ, λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό 직접 μ‘°μž‘ν•˜μ—¬ ν…ŒμŠ€νŠΈ ν™˜κ²½μ„ λ§Œλ“œλŠ” 것은 μ•„μ£Όμ•„μ£Ό λΉ„νš¨μœ¨μ μ΄λ©°, Unit Test의 원칙과도 λ§žμ§€ μ•ŠλŠ”λ‹€.

κ·Έλž˜μ„œ λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό 직접 μ‘°μž‘ν•˜λŠ” 것이 μ•„λ‹ˆλΌ ν…ŒμŠ€νŠΈν•  λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό λͺ¨μ‚¬ν•œ Mock 객체λ₯Ό λ§Œλ“€μ–΄ ν•΄λ‹Ή Mock κ°μ²΄μ—μ„œ ν…ŒμŠ€νŠΈ 상황을 λ§Œλ“€μ–΄ ν…ŒμŠ€νŠΈλ₯Ό μ§„ν–‰ν•΄μ•Ό ν•œλ‹€!!

Mocking & Mock

Β  β€œμš΄μ˜ ν™˜κ²½ λŒ€λΉ„ μ œμ•½μ΄ λ§Žμ€ ν…ŒμŠ€νŠΈ ν™˜κ²½μ—μ„œλŠ” μ‹€μ œ λ°μ΄ν„°λ² μ΄μŠ€μ™€ μ—°λ™ν•˜κ±°λ‚˜ μ‹€μ œ μ™ΈλΆ€ APIλ₯Ό ν˜ΈμΆœν•˜κΈ°κ°€ λΆˆκ°€λŠ₯ν•œ κ²½μš°κ°€ λ§ŽμŠ΅λ‹ˆλ‹€. κ°€λ Ή κ°€λŠ₯ν•˜λ”λΌλ„, μ΄λ ‡κ²Œ μ™ΈλΆ€ μ„œλΉ„μŠ€μ— μ˜μ‘΄ν•˜λŠ” ν…ŒμŠ€νŠΈλŠ” ν•΄λ‹Ή μ„œλΉ„μŠ€μ— λ¬Έμ œκ°€ μžˆμ„ 경우 깨질 수 있으며 μ‹€ν–‰ 속도도 느릴 수 밖에 μ—†μŠ΅λ‹ˆλ‹€.

Β  λ”°λΌμ„œ λ‹¨μœ„ ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•  λ•Œ 외뢀에 μ˜μ‘΄ν•˜λŠ” 뢀뢄을 μž„μ˜μ˜ κ°€μ§œ(Mock)둜 λŒ€μ²΄ν•˜λŠ” 기법이 자주 μ‚¬μš©λ˜λŠ”λ° 이λ₯Ό λͺ¨ν‚Ή(Mocking)이라고 ν•©λ‹ˆλ‹€. λ‹€μ‹œ 말해, λͺ¨ν‚Ή(Mocking)은 μ™ΈλΆ€ μ„œλΉ„μŠ€μ— μ˜μ‘΄ν•˜μ§€ μ•Šκ³  λ…λ¦½μ μœΌλ‘œ 싀행이 κ°€λŠ₯ν•œ λ‹¨μœ„ ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•˜κΈ° μœ„ν•΄μ„œ μ‚¬μš©λ˜λŠ” ν…ŒμŠ€νŒ… κΈ°λ²•μž…λ‹ˆλ‹€.” - article from here


Β  β€œμ˜ˆλ₯Ό λ“€μ–΄, λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ 데이터λ₯Ό μ‚­μ œν•˜λŠ” μ½”λ“œμ— λŒ€ν•œ λ‹¨μœ„ ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•  λ•Œ, μ‹€μ œ λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό μ‚¬μš©ν•œλ‹€λ©΄ μ—¬λŸ¬κ°€μ§€ 문제점이 λ°œμƒν•  수 μžˆμŠ΅λ‹ˆλ‹€.

  • λ°μ΄ν…Œλ² μ΄μŠ€ 접속과 같이 Networkμ΄λ‚˜ I/O μž‘μ—…μ΄ ν¬ν•¨λœ ν…ŒμŠ€νŠΈλŠ” μ‹€ν–‰ 속도가 ν˜„μ €νžˆ λ–¨μ–΄μ§ˆ 수 밖에 μ—†μŠ΅λ‹ˆλ‹€.
  • ν”„λ‘œμ νŠΈμ˜ 규λͺ¨κ°€ μΌœμ Έμ„œ ν•œ λ²ˆμ— μ‹€ν–‰ν•΄μ•Ό ν•  ν…ŒμŠ€νŠΈ μΌ€μ΄μŠ€κ°€ λ§Žμ΄μ§€λ©΄ μ΄λŸ¬ν•œ μž‘μ€ 속도 μ €ν•˜λ“€μ΄ λͺ¨μ—¬ 큰 μ΄μŠˆκ°€ 될 수 있으며, CI/CD νŒŒμ΄ν”„λΌμΈμ˜ μΌλΆ€λ‘œ ν…ŒμŠ€νŠΈκ°€ μžλ™ν™”λ˜μ–΄ 자주 μ‹€ν–‰λ˜μ•Ό ν•œλ‹€λ©΄ 더 큰 λ¬Έμ œκ°€ 될 수 μžˆμŠ΅λ‹ˆλ‹€.
  • ν…ŒμŠ€νŠΈ 자체λ₯Ό μœ„ν•œ μ½”λ“œλ³΄λ‹€ λ°μ΄ν„°λ² μ΄μŠ€μ™€ 연결을 λ§Ίκ³  νŠΈλžœμž­μ…˜μ„ μƒμ„±ν•˜κ³  쿼리λ₯Ό μ „μ†‘ν•˜λŠ” μ½”λ“œκ°€ 더 κΈΈμ–΄μ§ˆ 수 μžˆμŠ΅λ‹ˆλ‹€. 즉, 배보닀 배꼽이 더 컀질 수 μžˆμŠ΅λ‹ˆλ‹€.
  • λ§Œμ•½ ν…ŒμŠ€νŠΈ μ‹€ν–‰ μˆœκ°„ μΌμ‹œμ μœΌλ‘œ λ°μ΄ν„°λ² μ΄μŠ€κ°€ μ˜€ν”„λΌμΈ μž‘μ—… μ€‘μ΄μ—ˆλ‹€λ©΄ ν•΄λ‹Ή ν…ŒμŠ€νŠΈλŠ” μ‹€νŒ¨ν•˜κ²Œ λ©λ‹ˆλ‹€. λ”°λΌμ„œ ν…ŒμŠ€νŠΈκ°€ 인프라 ν™˜κ²½μ— 영ν–₯을 λ°›κ²Œλ©λ‹ˆλ‹€. (non-deterministic)
  • ν…ŒμŠ€νŠΈκ°€ μ’…λ£Œ 직 ν›„, λ°μ΄ν„°λ² μ΄μŠ€μ—μ„œ λ³€κ²½ 데이터λ₯Ό 직접 μ›λ³΅ν•˜κ±°λ‚˜ νŠΈλ Œμž­μ…˜μ„ rollback ν•΄μ€˜μ•Ό ν•˜λŠ”λ° μƒλ‹Ήνžˆ 번거둜운 μž‘μ—…μ΄ 될 수 μžˆμŠ΅λ‹ˆλ‹€.

무엇보닀 이런 λ°©μ‹μœΌλ‘œ ν…ŒμŠ€νŠΈλ₯Ό μž‘μ„±ν•˜κ²Œ 되면 νŠΉμ • κΈ°λŠ₯만 λΆ„λ¦¬ν•΄μ„œ ν…ŒμŠ€νŠΈν•˜κ² λ‹€λŠ” λ‹¨μœ„ ν…ŒμŠ€νŠΈ(Unit Test)의 근본적인 사상에 λΆ€ν•©ν•˜μ§€ μ•Šκ²Œ λ©λ‹ˆλ‹€.” - article from here

κ·Έλž˜μ„œ μ„œλ²„μ˜ μœ λ‹› ν…ŒμŠ€νŠΈλŠ” λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό 직접 μ‘°μž‘ν•˜λŠ” 것이 μ•„λ‹Œ μ„œλ²„λ₯Ό ν‰λ‚΄λ‚΄λŠ” MockRepositoryλ₯Ό λ§Œλ“€μ–΄ μ§„ν–‰ν•œλ‹€.


본래 service에선 Repository λ³€μˆ˜λ₯Ό λ§Œλ“€μ–΄ ν•΄λ‹Ή Repositoryλ₯Ό μ΄μš©ν•΄ λ°μ΄ν„°λ² μ΄μŠ€μ— μ ‘κ·Όν•œλ‹€. ν…ŒμŠ€νŠΈμ—μ„œλŠ” μ•„λž˜μ™€ 같이 Repositoryλ₯Ό λͺ¨μ‚¬ν•œ β€œMockRepositoryβ€œλ₯Ό λ§Œλ“ λ‹€.

예λ₯Ό λ“€μ–΄ UserRepositoryλ₯Ό λͺ¨μ‚¬ν•œ MockRepositoryλ₯Ό λ§Œλ“€μ–΄λ³΄μž.

class MockRepository {
  async findOneOrFail(query) {
    const user: User = new User();
    user.uuid = query.uuid;
    return user;
  }
}

describe('User', () => {
  let userService: UserService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UserService,
        {
          provide: getRepositoryToken(User),
          useClass: MockRepository,
        },
      ],
    }).compile();
    userService = module.get<UserService>(UserService);
  });

  it('should', async () => {
    const userId = '42';
    const result = await userService.findUserById(userId);
    expect(result.uuid).toBe(userId);
  });
});

Repositoryλ₯Ό λͺ¨μ‚¬ν•œ MockRepositoryλ₯Ό μš΄μš©ν•˜κΈ° μœ„ν•΄μ„œλŠ” Repository에 μ •μ˜λœ ν•¨μˆ˜λ“€μ„ μ„ μ–Έν•˜κ³  λͺ¨μ‚¬ν•΄μ€˜μ•Ό ν•œλ‹€. λ§Œμ•½ ν…ŒμŠ€νŠΈ ν•˜λ €λŠ” service의 νŠΉμ • ν•¨μˆ˜, 예λ₯Ό λ“€λ©΄ findUserById 같은 ν•¨μˆ˜κ°€ λ‚΄λΆ€μ—μ„œ Repository의 repository.findOne()κ³Ό 같은 ν•¨μˆ˜λ₯Ό μ‚¬μš©ν•œλ‹€λ©΄, MockRepositoryμ—μ„œ ν•΄λ‹Ή ν•¨μˆ˜λ₯Ό μ„ μ–Έν•΄μ€˜μ•Ό ν•œλ‹€λŠ” 말이닀!



Jest: spyOn

참고자료: link

1. jest.fn(); Mock function

jest.fn()을 μ΄μš©ν•΄ Mock function을 생성할 수 μžˆλ‹€.

const mockFn = jest.fn();

Mock function은 일반 ν•¨μˆ˜μ™€λŠ” 달리 λͺ¨ν‚Ήμ„ μ΄μš©ν•œ ν…ŒμŠ€νŠΈμ— νŠΉν™”ν•œ ν•¨μˆ˜λ₯Ό λͺ¨μ‚¬ν•œ β€˜κ°μ²΄β€™μž…λ‹ˆλ‹€.

* 인자 μž…λ ₯

mockFn()
mockFn(1)
mockFn("Lorem")
mockFn({ name: "Lorem", id: "Ipsum" })

* 리턴 κ°’ μ„€μ •

mockFn.mockReturnValue("Lorem Ipsum");
console.log(mockFn()); // "Lorem Ipsum"

* Mock 비동기 ν•¨μˆ˜

mockFn.mockResolvedValue("Async resolve value");
mockFn.then((result) => {
  console.log(result); // "Async resolved value"
})

* Mock function κ΅¬ν˜„

mockFn.mockImplementation((name) => `I am ${name}!`);
console.log(mockFn("Cantor")); // "I am Cantor!"

Mock function의 μœ μš©μ„±μ€ Mock function은 ν˜ΈμΆœμ— λŒ€ν•œ 정보λ₯Ό λͺ¨λ‘ κΈ°μ–΅ν•˜κ³  μžˆλ‹€λŠ” 점이닀!!

mockFn("a")
mockFn(["b", "c"])

expect(mockFn).toBeCalledTimes(2)
expect(mockFn).toBeCalledWith("a")
expect(mockFn).toBeCalledWith(["b", "c"])


2. jest.spyOn(); Spy Function

μ§€κΈˆκΉŒμ§€λŠ” λͺ¨λ‘ κΈ°μ‘΄ 객체을 λŒ€μ‹ ν•˜λŠ” β€˜λŒ€μ—­(代役)’인 Mock을 μ΄μš©ν•œ ν…ŒμŠ€νŠΈλ₯Ό μ‚΄νŽ΄λ΄€λ‹€. ν•˜μ§€λ§Œ λͺ‡λͺ‡ κ²½μš°μ—λŠ” κΈ°μ‘΄ 객체λ₯Ό Mock둜 λŒ€μ²΄ν•˜μ§€ μ–΄λ €μšΈ μˆ˜λ„ μžˆλ‹€. 이 경우 μ‚¬μš©ν•˜λŠ” 것이 λ°”λ‘œ β€œSpy Functionβ€œμ΄λ‹€!

예λ₯Ό λ“€μ–΄ μ•„λž˜μ™€ 같이 calculator에 μ •μ˜λœ add의 Spy Function을 λ§Œλ“€μ–΄ μ‚¬μš©ν•  수 μžˆλ‹€.

const calculator = {
  add: (a, b) => a + b,
}

const spyFn = jest.spyOn(calculator, "add")

const result = calculator.add(2, 3)

expect(spyFn).toBeCalledTimes(1)
expect(spyFn).toBeCalledWith(2, 3)
expect(result).toBe(5)

μ•žμ—μ„œ Spy function은 Mock ν•  수 없을 λ•Œ μ‚¬μš©ν•œλ‹€κ³  ν–ˆλ‹€. Mock ν•  수 μ—†λŠ” κ²½μš°λŠ”, β€œν…ŒμŠ€νŒ… λŒ€μƒ ν•¨μˆ˜ Aκ°€ λ‹€λ₯Έ ν•¨μˆ˜ B λ‚΄λΆ€μ—μ„œ 호좜되며 μ‚¬μš©λ˜λŠ” 상황이라 ν•¨μˆ˜ Aλ₯Ό mockingν•  경우, ν•¨μˆ˜ Bλ₯Ό ν…ŒμŠ€νŠΈν•  수 μ—†κΈ° λ•Œλ¬Έμ— 원본은 κ·ΈλŒ€λ‘œ 두고 Spy ν•œλ‹€.”라고 ν•œλ‹€.

> Mock vs. Spy link

이곳에 κ²Œμ‹œλœ .spyon() ν•¨μˆ˜μ˜ μ˜ˆμ‹œλ₯Ό μ‚΄νŽ΄λ³΄μž.

describe('UserService', () => {
  describe('μœ μ € 정보 μˆ˜μ •', () => {
    it('μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ” μœ μ € 정보λ₯Ό μˆ˜μ •ν•  경우 BadRequestError λ°œμƒν•œλ‹€.', async () => {
      const userId = faker.random.uuid();

      const updateUserDto: UpdateUserDto = {
        firstName: faker.lorem.sentence(),
        lastName: faker.lorem.sentence(),
        isActive: false,
      };

      const userRepositoryFindOneSpy = jest
        .spyOn(userRepository, 'findOne')
        .mockResolvedValue(null);

      try {
        await userService.updateUser(userId, updateUserDto);
      } catch (e) {
        expect(e).toBeInstanceOf(BadRequestException);
        expect(e.message).toBe(Message.NOT_FOUND_USER_ITEM);
      }

      expect(userRepositoryFindOneSpy).toHaveBeenCalledWith({
        where: {
          id: userId,
        },
      });
    });
  });
})

μ—¬κΈ°μ„œλ„ .spyOn()으둜 repository의 ν•¨μˆ˜λ₯Ό λͺ¨μ‚¬ν•˜λ”라도 ν•¨μˆ˜μ˜ λ‘œμ§μ„ μˆ˜μ •ν•˜λŠ” μž‘μ—…μ΄ ν•„μš”ν•˜λ‹€!!

λ˜ν•œ .spyOn()의 λ¦¬ν„΄μœΌλ‘œ 얻은 Spy Function κ°μ²΄λŠ” .toHaveBeenCalled...() ν•¨μˆ˜ λ“±μœΌλ‘œ ν…ŒμŠ€νŒ…ν•  ν•¨μˆ˜ λ‚΄λΆ€μ—μ„œ λͺ¨μ‚¬ν•œ ν•¨μˆ˜λ₯Ό μ˜¬λ°”λ₯΄κ²Œ μ‚¬μš©ν–ˆλŠ”μ§€λ₯Ό κ²€μ¦ν•˜λŠ” 데에 μ‚¬μš©λœλ‹€. μš°λ¦¬κ°€ 검증할 λŒ€μƒμ€ .spyOn()으둜 Mockingν•œ λŒ€μƒμ΄ μ•„λ‹ˆλ‹€. .spyOn()을 ν¬ν•¨ν•œ Mocking은 단지 μ˜μ‘΄μ„±μ„ 끊기 μœ„ν•œ μˆ˜λ‹¨μΌ 뿐이닀!!

κ²°κ΅­ .spyOn()을 μ‚¬μš©ν•˜λ”λΌλ„ κ²°κ΅­μ—” κΈ°μ‘΄ Mockingκ³Ό λΉ„μŠ·ν•œ λ§₯락으둜 ν…ŒμŠ€νŒ…μ΄ μ§„ν–‰λœλ‹€λŠ” 것이닀! μ‘°μ‚Όλͺ¨μ‚¬

Reference

Categories:

Updated: