[TIL] Unit Testing과 PATCH API

김시원·2023년 5월 26일
0

TIL

목록 보기
31/50

📌 Issues encountered

Issue #1

PATCH 요청에서 body 데이터 처리하기

Issue #2

Unit testing에서 실제 의존성 주입 대신, mock 객체로 대체하기

Nest can't resolve dependencies of the RequestsService (?, HospitalsRepository, EntityManager). Please make sure that the argument ReportsRepository at index [0] is available in the RootTestModule context.

    Potential solutions:
    - Is RootTestModule a valid NestJS module?
    - If ReportsRepository is a provider, is it part of the current RootTestModule?
    - If ReportsRepository is exported from a separate @Module, is that module imported within RootTestModule?
      @Module({
        imports: [ /* the Module containing ReportsRepository */ ]
      })

       8 |
       9 |   beforeEach(async () => {
    > 10 |     const moduleRef = await Test.createTestingModule({
         |                       ^
      11 |       controllers: [RequestsController],
      12 |       providers: [RequestsService],
      13 |     }).compile();

Issue #3

Transaction callback 함수 mocking하기

TypeError: this.entityManager.transaction is not a function

      22 |
      23 |   async createRequest(report_id: number, hospital_id: number) {
    > 24 |     const updatedReport = await this.entityManager.transaction(
         |                                                    ^
      25 |       'READ COMMITTED',
      26 |       async () => {
      27 |         try {

      at RequestsService.createRequest (requests/service/requests.service.ts:24:52)
      at Object.<anonymous> (requests/service/requests.service.spec.ts:69:44) 

Issue #4

비동기 함수를 mock할 때 mockResolvedValue()를 사용하였는데 해당 에러가 떴다. 실제 entity의 모든 값을 정의해야 한다는 것이다.

Argument of type '{ hospital_id: number; available_beds: number; }' is not assignable to parameter of type 'Hospitals | Promise<Hospitals>'.
  Type '{ hospital_id: number; available_beds: number; }' is missing the following properties from type 'Hospitals': name, address, phone, latitude, and 9 more.

Issue #5

GitHub Actions Build Error:

https://github.com/docker/build-push-action/issues/761

📌 What I tried

Issue #1

  • PATCH 요청에서 변경할 데이터들에 대해서 update용 DTO를 정의해준다.
  • npm i @nestjs/mapped-types 모듈을 통해 CreateReportDto에서 정의된 dto의 일부만을 가져와서 새로운 DTO를 정의해준다.
// update-report.dto.ts
import { PartialType } from '@nestjs/mapped-types';
import { CreateReportDto } from './create-report.dto';

export class UpdateReportDto extends PartialType(CreateReportDto) {
  longitude: number;
  latitude: number;
}
// report.controller.ts
@Patch(':report_id')
  updatePatientLocation(
    @Param('report_id') report_id: number,
    @Body() updatedLocation: UpdateReportDto,
  ) {
    this.logger.verbose('사용자 위치 변경 PATCH API');
    return this.reportsService.updatePatientLocation(
      report_id,
      updatedLocation,
    );
  }

Issue #2

controller를 unit testing하려고 했는데 service layer에 의존성 정의를 다 해주어야 했다.

  • requestService의 의존성
export class RequestsService {
  constructor(
    private readonly reportsRepository: ReportsRepository,
    private readonly hospitalsRepository: HospitalsRepository,
    private readonly entityManager: EntityManager,
  ) {}
}

RequestsService 클래스의 의존성인 reportsRepository, hospitalsRepository, entityManager도 직접 정의해서 RequestsService에 의존성을 넣어주지 않고, 이를 대체하는 mockRequestsService mock 객체를 선언해준다. 즉, 실제 service인 RequestsService 대신, mockRequestsService를 사용하는 것이다.

// request.controller.spce.ts
 beforeEach(async () => {
    const mockRequestsService = {
      createRequest: jest.fn().mockReturnValue({}),
    };

    const moduleRef = await Test.createTestingModule({
      controllers: [RequestsController],
      providers: [{ provide: RequestsService, useValue: mockRequestsService }],
    }).compile();

    requestsService = moduleRef.get(RequestsService);
    requestsController = moduleRef.get(RequestsController);
  });

Issue #3

{
  provide: EntityManager,
    useValue: {
      transaction: jest
        .fn()
        .mockImplementation((isolationLevel, callback) => {
        // transaction 메소드에 대한 Mock 구현
        return callback(); // 테스트 시에는 콜백 함수를 실행
      }),
   },
},

Issue #4

1) 먼저, 모든 프로퍼티들을 다 넣어줬다. 그랬더니, hadId, save같은 typeORM에서 제공하는 @Entity() 데코레이터가 자동으로 생성하는 메서드들도 정의를 해주어야된다고 에러가 떴다.
2) 모든 프로퍼티는 넣을 필요도 없을 뿐더러 위와 같은 에러가 떠서 다른 방식으로 해결하였다.
3) 일단, 비동기 함수의 리턴값을 mock하는 함수는 mockResolvedValue(returnValue), mockResolvedValueOnce(returnValue) 이 두가지가 있는데, 앞에 함수는 처음 리턴값을 정의하면 모든 호출에 대해 같은 값을 반환하지만, 뒤 함수는 매 호출마다 다른 리턴값을 정의해줄 수 있다. 따라서, 나의 경우 에러 케이스도 함께 테스트를 하고 있기 때문에 후자를 선택하였다.
4) Hospitals와 Reports entities의 일부 프로퍼티만 사용하기 위해 다음과 같인 리턴값을 설정해주었다.

jest
  .spyOn(hospitalsRepository, 'findHospital')
  .mockResolvedValueOnce(hospital as Hospitals);
jest
  .spyOn(reportsRepository, 'findReport')
  .mockResolvedValueOnce(report as Reports);

0개의 댓글