NestJS 공식문서 Testing

GGAE99·2023년 8월 1일
0

NestJS 공식 문서

목록 보기
14/33
post-thumbnail

Testing

자동화된 테스트는 진지한 소프트웨어 개발의 필수적인 부분 이라 간주됩니다. 테스트 자동화는 개발하는 도중 각각의 테스트, 혹은 테스트 묶음을 쉽게 혹은 빠르게 반복할 수 있게 해줍니다. 이는 Release가 품질과 성능 목표치를 충족하는지 보장하는데 도움을 줍니다. 테스트 자동화는 보증 범위를 늘려주며, 빠른 테스트 피드백 반복 주기를 개발자에게 제공합니다. 자동화는 개별 개발자의 생산성을 높이며 소스 코드 제어 check-in, 기능 통합 및 버전 릴리즈와 같은 중요한 개발 생명 주기 시점에서 테스트가 실행 될 수 있도록 보장합니다.

이러한 테스트들은 주로 단위 테스트, end-to-end (e2e) 테스트, 통합 테스트 등 여러 유형을 포함합니다. 이러한 장점들은 의심의 여지가 없지만, 설정하는 것은 번거로울 수 있습니다. Nest는 효과적인 테스트를 포함하여 개발 최상의 방법론을 촉진하기 위해 노력하고 있으며, 이를 도와주는 다음과 같은 기능을 제공합니다:

  • 컴포넌트에 대한 기본 단위 테스트와 애플리케이션에 대한 기본 e2e 테스트를 자동으로 생성합니다.
  • 독립적인 모듈/애플리케이션 로더를 빌드하는 테스트 러너와 같은 기본 도구를 제공합니다.
  • Jest, Supertest와 기본적으로 통합되어 있으며, 테스팅 도구에 대해 특정한 제한을 두지 않고 일관성을 유지합니다.
  • 테스팅 환경에서 Nest의 의존성 주입 시스템을 사용하여 구성 요소를 쉽게 모킹할 수 있도록 합니다.

언급한 바와 같이 Nest는 특정한 도구를 강요하지 않으므로 아무거나 원하는 테스팅 프레임워크를 사용할 수 있습니다. 필요한 요소(예: 테스트 러너)를 대체하기만 하면 Nest의 미리 준비된 테스팅 기능의 혜택을 그대로 누릴 수 있습니다.

Installation

시작에 앞서, 필요한 패키지를 설치하여야 합니다:

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

Unit testing

다음 예시에서는 두 개의 클래스, CatsControllerCatsService를 테스트합니다. 앞서 언급했듯이, Jest가 기본적으로 제공되는 테스팅 프레임워크로 사용됩니다. Jest는 테스트 러너 역할을 하면서도 assert function 기능과 mocking 과 spying, 및 기타 기능을 제공하는 테스트 유틸리티를 제공합니다. 이하 기본 테스트를 통해, 다음 기본 테스트에서, 이러한 클래스를 수동으로 인스턴스화하고 컨트롤러와 서비스를 실행하여 API가 제대로 동작하는지 확인합니다.

// cats.controller.spec.ts

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);
    });
  });
});

Hint!
테스트 파일은 테스트하는 클래스와 가까이 위치시키는 것이 좋습니다. 테스트 파일은 .spec 또는 .test 접미사를 가지도록 해야 합니다.

위의 예시는 간단하므로 Nest와 관련된 특정 기능을 테스트했다고 볼수는 없습니다. 실제로 의존성 주입을 사용하지도 않습니다(CatsService의 인스턴스를 catsController에 직접 전달하기만 했다는 것을 주목하세요). 이러한 형태의 테스팅은 테스트하는 클래스를 수동으로 인스턴스화하는 것으로, 이를 프레임워크에서 독립되어있다고 하여 독립 테스트 라고 명명하곤 합니다. 더 많은 Nest 기능을 활용하는 응용 프로그램을 테스트하는 데 도움이 되는 고급 기능을 소개하겠습니다.

Testing utilities

The @nestjs/testing 패키지는 보다 강력한 테스트 프로세스를 가능하게 하는 유틸리티를 제공합니다. 내장된 Test class 를 사용하여 이전 예제를 다시 작성해 보도록 하겠습니다.:

// cats.controller.spec.ts

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 moduleRef = await Test.createTestingModule({
        controllers: [CatsController],
        providers: [CatsService],
      }).compile();

    catsService = moduleRef.get<CatsService>(CatsService);
    catsController = moduleRef.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);
    });
  });
});

Test 클래스는 Nest의 전체 런타임을 모의화하는 실행 컨텍스트를 제공하는 유용한 도구입니다. 클래스 인스턴스를 쉽게 관리하고 mocking, overriding을 관리하는데 도움이되는 hook을 제공합니다. Test 클래스는 모듈 메타데이터 객체를 인수로 사용하는 createTestingModule() 메서드를 가지고 있습니다. (이 객체는 @Module() 데코레이터에 전달하는 것과 동일합니다.) 이 메서드는 TestingModule 인스턴스를 반환하며, 이 인스턴스는 몇 가지 메서드를 제공합니다. 유닛 테스트에서 중요한 메서드는 compile() 메서드입니다. 이 메서드는 모듈과 해당 의존성을 부트스트랩하고 (기존의 main.ts 파일에서 NestFactory.create()를 사용하여 애플리케이션을 부트스트랩하는 방식과 유사합니다), 테스트할 준비가 된 모듈을 반환합니다.

Hint!
compile() 메서드는 비동기이므로 await이 필요합니다. 모듈이 컴파일된 후 get() 메서드를 사용하여 선언된 모든 정적 인스턴스(컨트롤러 및 프로바이더)를 검색할 수 있습니다.

TestingModulemodule reference 클래스를 상속하므로 해당 scoped provider(transient or request-scoped)를 동적으로 해결할 수 있는 능력도 가지고 있습니다. 이를 resolve() 메서드를 사용하여 수행할 수 있습니다. (get() 메서드는 정적인 인스턴스만 검색할 수 있습니다.)

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

catsService = await moduleRef.resolve(CatsService);

Warning!!
resolve() 메서드는 프로바이더의 고유한 인스턴스를 반환하며, 해당 프로바이더의 DI 컨테이너 서브트리에서 이를 가져옵니다. 각 서브트리는 고유한 컨텍스트 식별자를 가지므로 이 메서드를 여러 번 호출하고 인스턴스 참조를 비교하면 동일하지 않음을 알 수 있습니다.

모든 프로바이더의 프로덕션 버전을 사용하는 대신 테스트 목적으로 커스텀 프로바이더를 사용하여 오버라이딩할 수 있습니다. 예를 들어 실제 데이터베이스에 연결하는 대신 데이터베이스 서비스를 모킹할 수 있습니다. 오버라이딩에 대해서는 다음 섹션에서 다루겠지만, 유닛 테스트에도 사용할 수 있습니다.

Auto mocking

Nest는 또한 누락된 모든 의존성에 적용할 mock factory를 정의할 수 있습니다. 이는 클래스에 많은 수의 의존성이 있는 경우에 유용하며, 모두를 모킹하는 데 오랜 시간과 많은 설정이 필요한 경우에 사용됩니다. 이 기능을 사용하려면 createTestingModule() 메서드에 useMocker() 메서드를 연결하고, 의존성 모의를 위한 팩토리를 전달해야 합니다. 이 팩토리는 선택적으로 인스턴스 토큰을 입력받으며, Nest 프로바이더로 유효한 모든 토큰을 사용할 수 있으며 모의 구현을 반환합니다. 아래는 jest-mock을 사용한 일반적인 모킹과 jest.fn()을 사용한 CatsService의 특정 모킹을 생성하는 예시입니다.

import { ModuleMocker, MockFunctionMetadata } from 'jest-mock';

const moduleMocker = new ModuleMocker(global);

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

  beforeEach(async () => {
    const moduleRef = await Test.createTestingModule({
      controllers: [CatsController],
    })
      .useMocker((token) => {
        const results = ['test1', 'test2'];
        if (token === CatsService) {
          return { findAll: jest.fn().mockResolvedValue(results) };
        }
        if (typeof token === 'function') {
          const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;
          const Mock = moduleMocker.generateFromMetadata(mockMetadata);
          return new Mock();
        }
      })
      .compile();

    controller = moduleRef.get(CatsController);
  });
});

이렇게 생성된 mocks는 일반적인 제공자와 마찬가지로 테스트 컨테이너에서 검색할 수 있습니다. moduleRef.get(CatsService)와 같이 사용합니다.

Hint!
@golevelup/ts-jestcreateMock와 같이 일반적인 mock 팩토리를 직접 전달할 수도 있습니다.

Hint!
REQUESTINQUIRER 프로바이더는 이미 컨텍스트에서 미리 정의되어 있기 때문에 자동으로 모킹될 수 없습니다. 그러나 이러한 프로바이더는 사용자 정의 프로바이더 구문이나 .overrideProvider 메서드를 사용하여 덮어쓸 수 있습니다.

End-to-end testing

단위 테스트가 개별 모듈과 클래스에 초점을 맞추는 것과는 달리, end-to-end (e2e) 테스트는 더 높은 수준에서 클래스와 모듈의 상호작용을 다룹니다. 즉, 이는 최종 사용자가 프로덕션 시스템과 상호작용하는 것과 유사한 상호작용을 다루는 것입니다. 응용 프로그램이 커지면 각 API 엔드포인트의 end-to-end 동작을 수동으로 테스트하는 것은 어려워집니다. 자동화된 end-to-end 테스트는 시스템의 전반적인 동작이 올바르고 프로젝트 요구사항을 충족하는지 보장하는 데 도움이 됩니다. e2e 테스트를 수행하기 위해서는 유닛 테스트와 비슷한 구성을 사용합니다. 게다가 Nest는 Supertest 라이브러리를 사용하여 HTTP 요청을 시뮬레이트하는 것을 쉽게 만들어줍니다.

// cats.e2e-spec.ts

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 moduleRef = await Test.createTestingModule({
      imports: [CatsModule],
    })
      .overrideProvider(CatsService)
      .useValue(catsService)
      .compile();

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

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

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

위 예시에서는 이전에 설명한 몇 가지 개념을 기반으로 합니다. 앞서 사용한 compile() 메서드 외에도, createNestApplication() 메서드를 사용하여 전체 Nest 런타임 환경을 인스턴스화합니다. 실행 중인 앱에 대한 참조를 app 변수에 저장하여 HTTP 요청을 시뮬레이트하는 데 사용합니다.

Supertest의 request() 함수를 사용하여 HTTP 테스트를 시뮬레이트합니다. 이러한 HTTP 요청은 실행 중인 Nest 앱으로 라우팅되도록 하기 위해 request() 함수에 Nest의 HTTP 리스너에 대한 참조를 전달합니다. 이로 인해 request(app.getHttpServer()) 구문이 생성되며, 이를 통해 실제 네트워크를 통해 수신되는 실제 HTTP 요청과 동일한 Nest 앱에 대한 요청을 초기화합니다. 예를 들어 request(...).get('/cats')을 사용하면 get '/cats' 같은 실제 인터넷을 통한 HTTP 요청과 동일한 Nest 앱에 대한 요청이 이루어집니다.

이 예시에서는 또한 CatsService의 대체 (테스트용) 구현을 제공합니다. 이 구현은 단순히 테스트용으로 사용할 하드코딩된 값을 반환합니다. 이러한 대체 구현을 제공하려면 overrideProvider()를 사용하면 됩니다. 마찬가지로 Nest는 overrideModule(), overrideGuard(), overrideInterceptor(), overrideFilter()overridePipe() 메서드를 사용하여 모듈, 가드, 인터셉터, 필터 및 파이프를 덮어쓸 수 있는 방법을 제공합니다.

각각의 override 메서드 (overrideModule() 제외)는 클래스를 제공하여 해당 객체(프로바이더, 가드 등)를 오버라이드할 인스턴스를 제공합니다.

  • useClass: 오버라이드할 객체(프로바이더, 가드 등)를 제공하기 위해 클래스를 제공합니다.
  • useValue: 오버라이드할 객체(프로바이더, 가드 등)를 제공하기 위해 인스턴스를 제공합니다.
  • useFactory: 오버라이드할 객체(프로바이더, 가드 등)를 제공하기 위해 인스턴스를 반환하는 함수를 제공합니다.

overrideModule()은 오버라이드할 원래 모듈을 제공하기 위해 useModule() 메서드와 함께 객체를 반환합니다.

const moduleRef = await Test.createTestingModule({
  imports: [AppModule],
})
  .overrideModule(CatsModule)
  .useModule(AlternateCatsModule)
  .compile();

각 override 메서드 유형은 다시 TestingModule 인스턴스를 반환하므로, 연쇄적인 방식으로 다른 메서드와 함께 연결할 수 있습니다. 이러한 체인의 끝에는 compile() 메서드를 사용하여 Nest가 모듈을 인스턴스화하고 초기화하도록 해야 합니다.

또한, 때로는 사용자 정의 로거를 사용하고자 할 수 있습니다. 예를들어. 테스트가 실행될 때 (예) CI 서버에서). setLogger() 메서드를 사용하고 LoggerService 인터페이스를 충족하는 객체를 전달하고 TestModuleBuilder 에 테스트 중 로그 방법을 지시합니다. (기본적으로, 'error'만 콘솔창에 표시됨).

Warning!!
@nestjs/core 패키지는 글로벌 강화자(global enhancer)를 정의하는 데 도움이 되도록 APP_ 접두사를 가진 고유한 프로바이더 토큰을 제공합니다. 이러한 토큰은 여러 프로바이더를 나타낼 수 있기 때문에 .overrideProvider(APP_GUARD)와 같이 오버라이드할 수 없습니다. 일부 글로벌 강화자를 오버라이드하려면 이러한 해결 방법을 따라야 합니다.

컴파일된 모듈은 다음 표에 설명된 몇 가지 유용한 메서드를 가지고 있습니다.

메서드설명
createNestApplication()주어진 모듈을 기반으로 Nest 애플리케이션 (INestApplication 인스턴스)을 생성하고 반환합니다. 애플리케이션을 init() 메서드를 사용하여 수동으로 초기화해야 합니다.
createNestMicroservice()주어진 모듈을 기반으로 Nest 마이크로서비스 (INestMicroservice 인스턴스)를 생성하고 반환합니다.
get()애플리케이션 컨텍스트에서 사용 가능한 컨트롤러 또는 프로바이더(가드, 필터 등)의 정적인 인스턴스를 검색합니다. 모듈 레퍼런스 클래스에서 상속됩니다.
resolve()애플리케이션 컨텍스트에서 동적으로 생성된 컨트롤러 또는 프로바이더(가드, 필터 등)의 인스턴스(리퀘스트 또는 트랜지언트)를 검색합니다. 모듈 레퍼런스 클래스에서 상속됩니다.
select()모듈의 의존성 그래프를 탐색하는 데 사용됩니다. 선택된 모듈에서 특정 인스턴스를 검색하는 데 사용할 수 있습니다. get() 메서드와 함께 strict mode (strict: true)를 사용하여 사용됩니다.

Hint!
e2e 테스트 파일은 test 디렉토리에 위치시키는 것이 좋습니다. 테스트 파일은 .e2e-spec 접미사를 가지도록 해야 합니다.

Overriding globally registered enhancers

전역으로 등록된 가드(또는 파이프, 인터셉터, 필터)가 있는 경우 해당 가드를 오버라이드하려면 몇 가지 추가 단계가 필요합니다. 처음에는 다음과 같이 원래 등록하는 것으로 시작합니다:

providers: [
  {
    provide: APP_GUARD,
    useClass: JwtAuthGuard,
  },
],

위 코드는 APP_* 토큰을 통해 가드를 "멀티"-프로바이더로 등록하는 것입니다. 여기서 JwtAuthGuard를 대체하려면, 등록은 이 슬롯에 기존의 프로바이더를 사용해야 합니다:

providers: [
  {
    provide: APP_GUARD,
    useExisting: JwtAuthGuard,
    // ^^^^^^^^ notice the use of 'useExisting' instead of 'useClass'
  },
  JwtAuthGuard,
],

Hint!
useClass 대신 useExisting을 사용하여 등록된 프로바이더를 참조하면 Nest가 토큰 뒤에 해당 인스턴스를 생성하지 않게 됩니다.

이제 JwtAuthGuard는 일반적인 프로바이더로 Nest에게 보이게 되며, TestingModule을 만들 때 이를 오버라이드할 수 있습니다:

const moduleRef = await Test.createTestingModule({
  imports: [AppModule],
})
  .overrideProvider(JwtAuthGuard)
  .useClass(MockAuthGuard)
  .compile();

이제 모든 테스트는 모든 요청에 대해 MockAuthGuard를 사용하게 됩니다.

Testing request-scoped instances

리퀘스트-스코프 프로바이더는 각 들어오는 요청마다 고유하게 생성됩니다. 이 인스턴스는 요청 처리가 완료된 후 가비지 컬렉션됩니다. 이는 테스트 중에 특정 요청에 대해 생성된 의존성 주입 서브트리에 접근할 수 없다는 문제를 야기합니다.

이전 섹션에서 설명한 대로 resolve() 메서드를 사용하여 동적으로 인스턴스화된 클래스를 검색할 수 있다는 것을 알고 있습니다. 또한 여기에서 설명한 대로 DI 컨테이너 서브트리의 라이프사이클을 제어하기 위해 고유한 컨텍스트 식별자를 전달할 수 있다는 것도 알고 있습니다. 그렇다면 이를 테스트 컨텍스트에서 어떻게 활용할까요?

활용을 위한 전략은 미리 컨텍스트 식별자를 생성하고 Nest에게 이 특정 ID를 사용하도록 강제하는 것입니다. 이렇게 하면 테스트된 요청에 대해 생성된 인스턴스를 검색할 수 있게 됩니다.

이를 위해 ContextIdFactory에 jest.spyOn()을 사용합니다:

const contextId = ContextIdFactory.create();
jest.spyOn(ContextIdFactory, 'getByRequest').mockImplementation(() => contextId);

이제 이 contextId를 사용하여 이후의 모든 요청에 대해 단일로 생성된 DI 컨테이너 서브트리에 액세스할 수 있습니다.

catsService = await moduleRef.resolve(CatsService, contextId);

질문 및 생각

  • Supertest?
    REST API에 대한 통합 테스트를 좀 더 간단하게 작성할 수 있도록 도와주는 라이브러리입니다.

  • Jest?
    테스팅 라이브러리

  • assert function
    테스트 코드 내에서 기대하는 값과 실제 결과를 비교하여 테스트의 성공 여부를 판단하는 함수입니다. 대표적인 Assertion 함수에는 expect, assert, should 등이 있습니다.

  • mocking
    테스트 중에 실제로 실행되는 코드 대신 가짜 코드를 사용하는 것을 말합니다. 일반적으로 외부 서비스, 라이브러리, 또는 의존성을 가지는 코드를 모킹하여 해당 코드가 의존하는 외부 요소와 상호작용하지 않고도 테스트할 수 있도록 합니다.

  • spying
    함수의 호출 및 호출에 대한 정보를 기록하고, 해당 함수가 실제로 실행되도록 허용하는 Mock 객체입니다. 함수의 호출 여부, 호출 횟수, 전달된 인자 등을 추적하고 기록할 수 있습니다.

  • resolve와 get의 차이점
    일반적으로, 테스트 환경에서는 get 메서드를 주로 사용합니다. 이는 테스트에서 모의(mock)된 서비스를 주입하거나, 필요한 서비스를 직접 등록하는 데 편리하기 때문입니다. 반면에, 리졸브 메서드는 주로 컨테이너의 타입 체크를 수행해야 하는 상황에서 사용됩니다.

  • Auto mocking?
    의존성을 해결해준다.

내 정리

테스팅이란 내가 작성한 메서드가 실제로 재대로 동작하는지 테스트 하는 코드이다.
예상 가능한 데이터를 반환하도록 설정하고, 테스트하는 형식이다.

  • unit 테스트: 도메인 모델과 비즈니스 로직을 테스트, 작은 단위의 코드 및 알고리즘 테스트
  • integration 테스트: 코드의 주요 흐름들을 통합적으로 테스트하며 주요 외부 의존성(ex. 데이터베이스)에 대해서 테스트
  • e2e 테스트: 최종 사용자의 흐름에 대한 테스트이며 외부로부터의 요청부터 응답까지 기능이 잘 동작하는지에 대한 테스트

nestJS / 'cannot find module' 에러 :

//package.json

// 변경 전 코드
  "jest": {
    "moduleFileExtensions": [
      "js",
      "json",
      "ts"
    ],
    "rootDir": "src",
    "testRegex": ".*\\.spec\\.ts$",
    "transform": {
      "^.+\\.(t|j)s$": "ts-jest"
    },
    "collectCoverageFrom": [
      "**/*.(t|j)s"
    ],
    "coverageDirectory": "../coverage",
    "testEnvironment": "node"
  }
//변경 후 코드
  "jest": {
    "moduleDirectories": [
      "node_modules",
      "src"
    ],
    "moduleFileExtensions": [
      "js",
      "json",
      "ts"
    ],
    "roots": [
      "src"
    ],
    "testRegex": ".spec.ts$",
    "transform": {
      "^.+\\.(t|j)s$": "ts-jest"
    },
    "coverageDirectory": "../coverage",
    "testEnvironment": "node",
    "moduleNameMapper": {
      "src/(.*)": "<rootDir>/src/$1"
    }
  }

// 경로 변경이 핵심인 듯 하다.

jest를 활용한 mocking

jest.mock("../user.module");
jest.mock("../user.service");

e2e test 에러 :

import * as request from 'supertest';
import { INestApplication } from "@nestjs/common";
import { UserService } from "../user.service";
import { Test } from "@nestjs/testing";
import { UserModule } from "../user.module";
import { User } from "../entity/user.entity";
import { ROLE } from "../constant/user.role";

jest.mock("../user.module");
jest.mock("../user.service");

describe('User', () => {

    const dummyUser: User = new User();
    dummyUser.id = '1';
    dummyUser.email = 'test@example.com';
    dummyUser.password = 'hashed_password';
    dummyUser.role = ROLE.ADMIN; // ROLE.ADMIN 또는 다른 ROLE 값으로 설정
    dummyUser.refreshToken = null;

    const dummyUserTwo: User = new User();
    dummyUserTwo.id = '2';
    dummyUserTwo.email = 'test2@example.con';
    dummyUserTwo.password = 'hashed_password';
    dummyUserTwo.role = ROLE.USER; // ROLE.ADMIN 또는 다른 ROLE 값으로 설정
    dummyUserTwo.refreshToken = null;

    // 'dummyUser'를 단일 요소를 가지는 배열로 만들기
    const users: User[] = [dummyUser, dummyUserTwo];

    let app: INestApplication;
    let userService = { getAllUser: () => users };

    beforeAll(async () => {
        const moduleRef = await Test.createTestingModule({
            imports: [UserModule],
        })
            .overrideProvider(UserService)
            .useValue(userService)
            .compile();

        app = moduleRef.createNestApplication(); // 런타임 환경 인스턴스화
        await app.init();
    });

    it('/GET allUsers', () => {
        return request(app.getHttpServer())
            .get('/allUsers') // 요청 경로를 '/allUsers'로 수정
            .expect(200) // < = 에러 발생
            .expect({ 
                data: userService.getAllUser(),
            });
    });

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

분명히 controller에 해당 경로가 있는데, 404에러가 나옵니다ㅠㅠㅠ.

0개의 댓글