포시코딩

[Jest] 오토 모킹 vs 수동 모킹 본문

Test Case

[Jest] 오토 모킹 vs 수동 모킹

포시 2023. 4. 2. 14:01
728x90

오토 모킹

import { Test, TestingModule } from '@nestjs/testing';
import { ModuleMocker, MockFunctionMetadata } from 'jest-mock';

const moduleMocker = new ModuleMocker(global);
// ModuleMocker는 getMetadata 메서드를 사용해 모듈의 메타데이터를 생성하고
// generateFromMetadata 메서드를 사용해 메타데이터를 바탕으로 모듈을 모킹한다.
// 즉, 오토 모킹의 핵심

describe('ChatService', () => {
  let service: ChatService;  // ChatService 인스턴스
  let mockChatRepository: jest.Mocked<ChatRepository>;  // ChatRepository의 모킹된 버전

  beforeEach(async () => {  // 각각의 테스트 코드 전에 실행
    jest.clearAllMocks();  // 이전에 모킹된 모든 함수 초기화
    const module: TestingModule = await Test.createTestingModule({  
      providers: [ChatService],  // ChatService를 포함한 providers를 생성
    }).useMocker((token) => {  // providers를 모킹(Mocking). token은 
      // useMocker는 콜백 함수를 인자로 받는데 (token), 이 콜백 함수는 provider의 종류에 따라 다른 타입을 가지며, 주로 함수 타입을 가진다.
      if (typeof token === 'function') {  // provider가 함수일 경우
        const mockMetadata = moduleMocker.getMetadata(token) as MockFunctionMetadata<any, any>;  // 함수의 메타데이터(MockFunctionMetadata)를 가져온다.
        const Mock = moduleMocker.generateFromMetadata(mockMetadata);  // 메타데이터를 바탕으로 모킹된 함수 생성
        return new Mock();  // 인스턴스를 생성하고 반환
        // 이렇게 모킹된 함수는 useMocker() 함수가 호출된 createTestingModule() 함수 안에 있는 
        // providers 중에서 해당 함수와 일치하는 타입의 provider를 대체(Mocking)한다.
      }
    }).compile();  // 컴파일된 TestingModule을 반환

    service = module.get(ChatService);  // service 변수에 위에서 선언된 ChatService 인스턴스 할당
    mockChatRepository = module.get(ChatRepository);  // mockChatRepository에 ChatRepository의 모킹된 버전 할당

    // 이후부터 service의 메서드를 호출해 ChatService의 메서드를 호출 할 수 있고
    // mockChatRepository 변수를 사용해 ChatRepository의 모킹된(Mocked) 메서드를 호출하며 테스트를 진행할 수 있다.
  });

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

  describe('getChatRooms Method', () => {
    const userId = 1;
    it('success', async () => {
      const mockReturnValue = [new Meetup()];
      mockChatRepository.getChatRooms.mockResolvedValue(mockReturnValue);
      // mockChatRepository의 getChatRooms 메서드에 대해 리턴타입이 Promise인 것을 감안해 mockResolvedValue를 사용하는 부분
      // mockResolvedValue 함수는 특정 함수가 비동기 함수인 경우, 해당 함수가 호출되면 반환되는 값을 프로미스 형태로 반환하도록 설정한다.
      // 이를 통해 테스트에서 특정 비동기 함수의 반환 값을 쉽게 모킹(Mocking) 할 수 있다.
      const result = await service.getChatRooms(userId);

      expect(result).toBe(mockReturnValue);
      expect(mockChatRepository.getChatRooms).toHaveBeenCalled();
      expect(mockChatRepository.getChatRooms).toHaveBeenCalledWith(userId);
      expect(result).toBeInstanceOf(Array);
    });
  });
}

 

수동 모킹

import { Test, TestingModule } from '@nestjs/testing';

describe('ChatService', () => {
  let service: ChatService;
  let mockChatRepository: ChatRepository;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        ChatService,
        {
          provide: ChatRepository,
          useValue: {
            getChatRooms: jest.fn(),
            exitChatRoom: jest.fn(),
            insert: jest.fn(),
            find: jest.fn(),
          }
        }
      ],
    }).compile();

    service = module.get(ChatService);
    mockChatRepository = module.get(ChatRepository);
  });

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

  describe('getChatRooms Method', () => {
    const userId = 1;
    it('success', async () => {
      const mockReturnValue = [new Meetup()];
      jest.spyOn(mockChatRepository, 'getChatRooms').mockResolvedValue(mockReturnValue);
      const result = await service.getChatRooms(userId);

      expect(result).toBe(mockReturnValue);
      expect(mockChatRepository.getChatRooms).toHaveBeenCalled();
      expect(mockChatRepository.getChatRooms).toHaveBeenCalledWith(userId);
      expect(result).toBeInstanceOf(Array);
    });
  });
});

 

차이점

  • 오토 모킹할 땐 provide된 ChatRepository의 각 메서드들에 대해 하나하나 세팅할 필요가 없었던 반면
    수동에선 provide와 useValue를 통해 chatRepository에 대한 가짜 객체를 만든다.
  • 수동에선 chatRepository의 모든 함수를 jest.fn()을 통해 모킹한다.
  •  
mockChatRepository.getChatRooms.mockResolvedValue(mockReturnValue);

오토 모킹에선 위의 방법으로 반환 값을 모킹한 반면

 

jest.spyOn(mockChatRepository, 'getChatRooms').mockResolvedValue(mockReturnValue);

수동 모킹에선 jest의 spyOn 함수를 통해 mockChatRepository의 getChatRooms 메서드에 대해 반환 값을 모킹하고 있다.

728x90

'Test Case' 카테고리의 다른 글

[Jest] mockImplementation, mockResolvedValue  (0) 2023.07.17
테스트 코드(Test Code) - supertest  (0) 2023.01.08
테스트 코드(Test Code) - Jest  (0) 2022.12.31
assert문이란?  (0) 2022.11.19
코드 커버리지(Code Coverage)란?  (0) 2022.11.19