Testing

Testing

์ž๋™ ํ…Œ์ŠคํŠธ๋Š” ๋ชจ๋“  ๊ธฐ๋Šฅ์„ ๊ฐ–์ถ˜ ์†Œํ”„ํŠธ์›จ์–ด ์ œํ’ˆ์˜ ํ•„์ˆ˜ ๋ถ€๋ถ„์ž…๋‹ˆ๋‹ค. ์‹œ์Šคํ…œ์˜ ๊ฐ€์žฅ ๋ฏผ๊ฐํ•œ ๋ถ€๋ถ„์„ ๋‹ค๋ฃจ๋Š” ๊ฒƒ์ด ๋งค์šฐ ์ค‘์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ชฉํ‘œ๋ฅผ ๋‹ฌ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ํ†ตํ•ฉ ํ…Œ์ŠคํŠธ, ๋‹จ์œ„ ํ…Œ์ŠคํŠธ, e2e ํ…Œ์ŠคํŠธ ๋“ฑ๊ณผ ๊ฐ™์€ ๋‹ค์–‘ํ•œ ํ…Œ์ŠคํŠธ ์„ธํŠธ๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  Nest๋Š” ํ…Œ์ŠคํŠธ ๊ฒฝํ—˜์„ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ๋‹ค์–‘ํ•œ ํ…Œ์ŠคํŠธ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.

์ผ๋ฐ˜์ ์œผ๋กœ, ๋‹น์‹ ์€ ํ…Œ์ŠคํŒ… ํ”„๋ ˆ์ž„ ์›Œํฌ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ํˆด๋ง์„ ์‹œํ–‰ํ•˜์ง€ ์•Š์œผ๋ฉฐ ์š”๊ตฌ ์‚ฌํ•ญ์— ๋งž๋Š” ๊ฒƒ์„ ์„ ํƒํ•˜์‹ญ์‹œ์˜ค. ๊ธฐ๋ณธ Nest ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์Šคํƒ€ํ„ฐ๋Š” Jest ํ”„๋ ˆ์ž„ ์›Œํฌ์™€ ํ†ตํ•ฉ๋˜์–ด ํ…Œ์ŠคํŠธ ์ž‘์„ฑ์„ ์‹œ์ž‘ํ•  ๋•Œ ๋ฐœ์ƒํ•˜๋Š” ์˜ค๋ฒ„ ํ—ค๋“œ๋ฅผ ์ค„์ด์ง€๋งŒ ์—ฌ์ „ํžˆ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค๋ฅธ ๋„๊ตฌ๋ฅผ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค.

Installation

๋จผ์ € ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.

$ npm i --save-dev @nestjs/testing

Unit testing

๋‹ค์Œ ์˜ˆ์ œ์—์„œ๋Š” ๊ฐ๊ฐ CatsController์™€ CatsService๋ผ๋Š” ๋‘ ๊ฐ€์ง€ ํด๋ž˜์Šค๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์•ž์—์„œ ์–ธ๊ธ‰ํ–ˆ๋“ฏ์ด Jest๋Š” ๋ณธ๊ฒฉ์ ์ธ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„ ์›Œํฌ๋กœ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด ํ”„๋ ˆ์ž„ ์›Œํฌ๋Š” ํ…Œ์ŠคํŠธ ์‹คํ–‰๊ธฐ์ฒ˜๋Ÿผ ์ž‘๋™ํ•˜๋ฉฐ, ๋ชจ์˜, ๊ฐ์‹œ ๋“ฑ์„ ๋•๋Š” assert ํ•จ์ˆ˜ ๋ฐ test-doubles ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ์ผ๋‹จ ํ˜ธ์ถœ ๋œ result๋ณ€์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋„๋กcatsService.findAll()๋ฉ”์†Œ๋“œ๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค. ๋•๋ถ„์— catsController.findAll()์ด ์˜ˆ์ƒ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š”์ง€ ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

@@filename(cats.controller.spec)
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

describe('CatsController', () => {
  let catsController: CatsController;
  let catsService: CatsService;

  beforeEach(() => {
    catsService = new CatsService();
    catsController = new CatsController(catsService);
  });

  describe('findAll', () => {
    it('should return an array of cats', async () => {
      const result = ['test'];
      jest.spyOn(catsService, 'findAll').mockImplementation(() => result);

      expect(await catsController.findAll()).toBe(result);
    });
  });
});
@@switch
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

describe('CatsController', () => {
  let catsController;
  let catsService;

  beforeEach(() => {
    catsService = new CatsService();
    catsController = new CatsController(catsService);
  });

  describe('findAll', () => {
    it('should return an array of cats', async () => {
      const result = ['test'];
      jest.spyOn(catsService, 'findAll').mockImplementation(() => result);

      expect(await catsController.findAll()).toBe(result);
    });
  });
});

info ํžŒํŠธ ํ…Œ์ŠคํŠธ ํŒŒ์ผ์€ ํ…Œ์ŠคํŠธ๋œ ํด๋ž˜์Šค ๊ทผ์ฒ˜์— ๋ณด๊ด€ํ•˜์‹ญ์‹œ์˜ค. ํ…Œ์ŠคํŠธ ํŒŒ์ผ์—๋Š” .spec ๋˜๋Š” .test ์ ‘๋ฏธ์‚ฌ๊ฐ€ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

์šฐ๋ฆฌ๋Š” ์ง€๊ธˆ๊นŒ์ง€ ๊ธฐ์กด Nest ํ…Œ์ŠคํŠธ ์œ ํ‹ธ๋ฆฌํ‹ฐ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ํ…Œ์ŠคํŠธ ํ•œ ํด๋ž˜์Šค์˜ ์ธ์Šคํ„ด์Šคํ™”๋ฅผ ์ˆ˜๋™์œผ๋กœ ์ฒ˜๋ฆฌ ํ–ˆ์œผ๋ฏ€๋กœ ์œ„์˜ ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ๋Š” Nest์™€ ๊ด€๋ จ์ด ์—†์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์œ ํ˜•์˜ ํ…Œ์ŠคํŠธ๋ฅผ isolated ํ…Œ์ŠคํŠธ๋ผ๊ณ ํ•ฉ๋‹ˆ๋‹ค.

Testing utilities

@nestjs/testing ํŒจํ‚ค์ง€๋Š” ํ…Œ์ŠคํŠธ ํ”„๋กœ์„ธ์Šค๋ฅผ ํ–ฅ์ƒ์‹œํ‚ค๋Š” ์œ ํ‹ธ๋ฆฌํ‹ฐ ์„ธํŠธ๋ฅผ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋…ธ์ถœ ๋œTest ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ด์ „ ์˜ˆ์ œ๋ฅผ ๋‹ค์‹œ ์ž‘์„ฑํ•ด ๋ด…์‹œ๋‹ค.

@@filename(cats.controller.spec)
import { Test } from '@nestjs/testing';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

describe('CatsController', () => {
  let catsController: CatsController;
  let catsService: CatsService;

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

    catsService = module.get<CatsService>(CatsService);
    catsController = module.get<CatsController>(CatsController);
  });

  describe('findAll', () => {
    it('should return an array of cats', async () => {
      const result = ['test'];
      jest.spyOn(catsService, 'findAll').mockImplementation(() => result);

      expect(await catsController.findAll()).toBe(result);
    });
  });
});
@@switch
import { Test } from '@nestjs/testing';
import { CatsController } from './cats.controller';
import { CatsService } from './cats.service';

describe('CatsController', () => {
  let catsController;
  let catsService;

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

    catsService = module.get(CatsService);
    catsController = module.get(CatsController);
  });

  describe('findAll', () => {
    it('should return an array of cats', async () => {
      const result = ['test'];
      jest.spyOn(catsService, 'findAll').mockImplementation(() => result);

      expect(await catsController.findAll()).toBe(result);
    });
  });
});

Test ํด๋ž˜์Šค์—๋Š” ๋ชจ๋“ˆ ๋ฉ”ํƒ€ ๋ฐ์ดํ„ฐ ( @Module) ๋ฐ์ฝ”๋ ˆ์ดํ„ฐ์— ์ „๋‹ฌ๋œ ๊ฒƒ๊ณผ ๋™์ผํ•œ ๊ฐ์ฒด)๋ฅผ ์ธ์ˆ˜๋กœ ์ทจํ•˜๋Š”createTestingModule()๋ฉ”์†Œ๋“œ๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๋ฉ”์†Œ๋“œ๋Š” TestingModule ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ค์–ด ๋ช‡ ๊ฐœ์˜ ๋ฉ”์†Œ๋“œ๋ฅผ ์ œ๊ณตํ•˜์ง€๋งŒ ๋‹จ์œ„ ํ…Œ์ŠคํŠธ์™€ ๊ด€๋ จํ•˜์—ฌ ์ปดํŒŒ์ผ ์ค‘ ํ•˜๋‚˜ ์ธ compile()๋งŒ ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ธฐ๋Šฅ์€ ๋น„๋™๊ธฐ์ด๋ฏ€๋กœ ๊ธฐ๋‹ค๋ ค์•ผํ•ฉ๋‹ˆ๋‹ค. ๋ชจ๋“ˆ์ด ์ปดํŒŒ์ผ๋˜๋ฉด, get() ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ชจ๋“  ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฒ€์ƒ‰ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์‹ค์ œ ์ธ์Šคํ„ด์Šค๋ฅผ ๋ชจ๋ฐฉํ•˜๊ธฐ ์œ„ํ•ด custom provider๋กœ ๊ธฐ์กด ๊ณต๊ธ‰์ž๋ฅผ ์žฌ์ •์˜ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

End-to-end testing

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ปค์ง€๋ฉด ๊ฐ API ์—”๋“œ ํฌ์ธํŠธ์˜ ๋™์ž‘์„ ์ˆ˜๋™์œผ๋กœ ํ…Œ์ŠคํŠธํ•˜๊ธฐ๊ฐ€ ์–ด๋ ต์Šต๋‹ˆ๋‹ค. ์—”๋“œ ํˆฌ ์—”๋“œ ํ…Œ์ŠคํŠธ๋Š” ๋ชจ๋“  ๊ฒƒ์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ž‘๋™ํ•˜๊ณ  ํ”„๋กœ์ ํŠธ ์š”๊ตฌ ์‚ฌํ•ญ์— ๋งž๋Š”์ง€ ํ™•์ธํ•˜๋Š” ๋ฐ ๋„์›€์ด๋ฉ๋‹ˆ๋‹ค. e2e ํ…Œ์ŠคํŠธ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ธฐ ์œ„ํ•ด unit testing์˜ ๊ฒฝ์šฐ์™€ ๋™์ผํ•œ ๊ตฌ์„ฑ์„ ์‚ฌ์šฉํ•˜์ง€๋งŒ HTTP ์š”์ฒญ์„ ์‹œ๋ฎฌ๋ ˆ์ด์…˜ ํ•  ์ˆ˜์žˆ๋Š” supertest ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค. .

@@filename(cats.e2e-spec)
import * as request from 'supertest';
import { Test } from '@nestjs/testing';
import { CatsModule } from '../../src/cats/cats.module';
import { CatsService } from '../../src/cats/cats.service';
import { INestApplication } from '@nestjs/common';

describe('Cats', () => {
  let app: INestApplication;
  let catsService = { findAll: () => ['test'] };

  beforeAll(async () => {
    const module = await Test.createTestingModule({
      imports: [CatsModule],
    })
      .overrideProvider(CatsService)
      .useValue(catsService)
      .compile();

    app = module.createNestApplication();
    await app.init();
  });

  it(`/GET cats`, () => {
    return request(app.getHttpServer())
      .get('/cats')
      .expect(200)
      .expect({
        data: catsService.findAll(),
      });
  });

  afterAll(async () => {
    await app.close();
  });
});
@@switch
import * as request from 'supertest';
import { Test } from '@nestjs/testing';
import { CatsModule } from '../../src/cats/cats.module';
import { CatsService } from '../../src/cats/cats.service';
import { INestApplication } from '@nestjs/common';

describe('Cats', () => {
  let app: INestApplication;
  let catsService = { findAll: () => ['test'] };

  beforeAll(async () => {
    const module = await Test.createTestingModule({
      imports: [CatsModule],
    })
      .overrideProvider(CatsService)
      .useValue(catsService)
      .compile();

    app = module.createNestApplication();
    await app.init();
  });

  it(`/GET cats`, () => {
    return request(app.getHttpServer())
      .get('/cats')
      .expect(200)
      .expect({
        data: catsService.findAll(),
      });
  });

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

info ํžŒํŠธ e2e ํ…Œ์ŠคํŠธ ํŒŒ์ผ์„ e2e ๋””๋ ‰ํ† ๋ฆฌ ์•ˆ์— ๋ณด๊ด€ํ•˜์‹ญ์‹œ์˜ค. ํ…Œ์ŠคํŠธ ํŒŒ์ผ์—๋Š” .e2e-spec ๋˜๋Š” .e2e-test ์ ‘๋ฏธ์‚ฌ๊ฐ€ ์žˆ์–ด์•ผํ•ฉ๋‹ˆ๋‹ค.

cats.e2e-spec.ts ํ…Œ์ŠคํŠธ ํŒŒ์ผ์€ ๋‹จ์ผ HTTP ์—”๋“œ ํฌ์ธํŠธ ํ…Œ์ŠคํŠธ (/cats)๋ฅผ ํฌํ•จํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š”app.getHttpServer()๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Nest ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ์‹คํ–‰๋˜๋Š” ๊ธฐ๋ณธ HTTP ์„œ๋ฒ„๋ฅผ ์„ ํƒํ–ˆ์Šต๋‹ˆ๋‹ค. TestingModule ์ธ์Šคํ„ด์Šค๋Š” overrideProvider()๋ฉ”์†Œ๋“œ๋ฅผ ์ œ๊ณตํ•˜๋ฏ€๋กœ ๊ฐ€์ ธ์˜จ ๋ชจ๋“ˆ์— ์˜ํ•ด ์„ ์–ธ ๋œ ๊ธฐ์กด ์ œ๊ณต์ž๋ฅผ ์˜ค๋ฒ„๋ผ์ด๋“œ ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ ํ•ด๋‹น ๋ฉ”์†Œ๋“œ ์ธ overrideGuard(), overrideInterceptor (), overrideFilter()๋ฐ overridePipe()๋ฅผ ๊ฐ๊ฐ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ€๋“œ, ์ธํ„ฐ์…‰ํ„ฐ, ํ•„ํ„ฐ ๋ฐ ํŒŒ์ดํ”„๋ฅผ ์—ฐ์†์ ์œผ๋กœ ๋Œ€์ฒด ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ปดํŒŒ์ผ๋œ ๋ชจ๋“ˆ์—๋Š” ๋‹ค์Œ ํ‘œ์— ์ž˜ ์„ค๋ช… ๋œ ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ๋ฐฉ๋ฒ•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

createNestApplicaton()

์ฃผ์–ด์ง„ ๋ชจ๋“ˆ์„ ๊ธฐ๋ฐ˜์œผ๋กœ Nest ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค (INestApplication ๋ฐ˜ํ™˜). init() ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์ˆ˜๋™์œผ๋กœ ์ดˆ๊ธฐํ™”ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

createNestMicroservice()

์ง€์ •๋œ ๋ชจ๋“ˆ์„ ๊ธฐ๋ฐ˜์œผ๋กœ Nest ๋งˆ์ดํฌ๋กœ ์„œ๋น„์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ๋งŒ๋“ญ๋‹ˆ๋‹ค (INestMicroservice๋ฅผ ๋ฐ˜ํ™˜ ํ•จ).

get()

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ปจํ…์ŠคํŠธ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ์ปจํŠธ๋กค๋Ÿฌ ๋˜๋Š” ์ œ๊ณต์ž (๊ฐ€๋“œ, ํ•„ํ„ฐ ๋“ฑ ํฌํ•จ)์˜ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.

select()

์˜ˆ๋ฅผ ๋“ค์–ด, ๋ชจ๋“ˆ ๊ทธ๋ž˜ํ”„๋ฅผ ํƒ์ƒ‰ํ•˜์—ฌ ์„ ํƒํ•œ ๋ชจ๋“ˆ์—์„œ ํŠน์ • ์ธ์Šคํ„ด์Šค๋ฅผ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค (get() ๋ฉ”์„œ๋“œ์—์„œ ํ™œ์„ฑํ™” ๋œ ์—„๊ฒฉ ๋ชจ๋“œstrict:true์™€ ํ•จ๊ป˜ ์‚ฌ์šฉ).

Last updated

Was this helpful?