Engineering/etc.

프론트엔드 테스트 Mock 목 활용하기

728x90
반응형

Mocking (모킹)

(mock: 모조품)

테스트할 때 의존하는 내부 또는 외부 요소를 가짜로 만들어 사용하는 방식입니다. 모킹은 실제 객체를 사용하지 않고, 해당 객체의 동작만을 흉내냄으로써 테스트를 독립적으로 수행할 수 있도록 합니다.

  • 모킹의 활용성: 테스트뿐만 아니라 로컬 개발 환경에서도 생산성을 높입니다. 예를 들어, JSONPlaceholder와 같은 가짜 API를 사용해 빠르게 테스트할 수 있습니다.
  • 독립적인 테스트: 상용 API, 데이터베이스, 라이브러리 등의 의존성 없이 코드의 동작을 독립적으로 테스트할 수 있습니다. 이는 테스트의 신뢰성을 높여줍니다.
  • 모킹의 필요성: 모킹을 사용하지 않으면 테스트 시간이 길어지고, 모든 인터페이스가 구현된 환경에서 테스트를 수행해야 하는 번거로움이 생깁니다. 필요한 경우 모킹을 적절히 활용하는 것이 효율적인 테스트 방법입니다.

Mock의 장단점

  • 장점
    • 독립성: 외부 리소스에 의존하지 않고 테스트할 수 있어 안정성이 높아집니다.
    • 속도 향상: 외부 API나 데이터베이스 호출 없이 테스트가 가능하므로 빠르게 테스트를 진행할 수 있습니다.
    • 일관성: 테스트 결과를 예측하기 쉬우며, 오류 상황을 쉽게 시뮬레이션할 수 있습니다.
  • 단점
    • 실제 환경과의 차이: 실제 실행 환경과 다른 조건에서 테스트를 수행하기 때문에, 테스트와 실제 시스템 간의 동작이 다를 수 있습니다.

목 객체 용어 (책 내용)

  • 스텁(Stub): 테스트를 위해 특정 의존성을 대체하는 객체입니다. (대역)
    • 주로 의존하고 있는 컴포넌트에서 테스트하기 어려운 부분이 있을 때 사용합니다.
    • Jest에서는 목 모듈 API를 사용하여 특정 모듈이나 라이브러리를 스텁으로 대체합니다.
    • 예시: 웹 API에 의존하는 로직을 테스트할 때, ‘웹 API가 특정 값을 반환 했을 때 이렇게 동작해야 한다’는 식으로 테스트를 수행할 수 있습니다.
    jest.mock("next/router", () => require("next-router-mock"));
    
  • 스파이(Spy): 호출된 함수의 기록을 검증하는 데 사용됩니다.
    • Jest에서는 목 함수 API를 사용하여 함수 호출 및 전달된 인수를 기록합니다.
    • 예시: 콜백 함수가 의도한 대로 호출 되었는지, 호출된 횟수나 인수를 기록하여 테스트할 수 있습니다.

Mock을 활용하는 상황들

  • API 응답 테스트: 실제 외부 서버나 API에 요청을 보내는 대신, 가짜 응답을 만들어 테스트합니다.
  • 의존성 격리: 테스트하려는 모듈이 다른 모듈에 의존하는 경우, 해당 의존성을 mock으로 대체하여 해당 모듈만 집중적으로 테스트합니다.
  • 특정 상황 가정: 테스트 환경에서 조건을 설정하고 특정 상황을 가정한 상태에서 동작을 테스트합니다.

Mock 테스트 예시 코드

1. 특정 API를 Mocking

Jest의 목 모듈 jest.mock을 사용하여 외부 API를 대체합니다. https://jestjs.io/docs/mock-functions#mocking-modules

import axios from 'axios';

class Users {
  static all() {
    return axios.get('/users.json').then(resp => resp.data);
  }
}

export default Users;
import axios from 'axios';
import Users from './users';

jest.mock('axios');

test('should fetch users', () => {
  const users = [{name: 'Bob'}];
  const resp = {data: users};
  axios.get.mockResolvedValue(resp);

  // or you could use the following depending on your use case:
  // axios.get.mockImplementation(() => Promise.resolve(resp))

  return Users.all().then(data => expect(data).toEqual(users));
});

2. 특정 함수를 Mocking

jest.fn 이나 jest.spyOn 를 사용하여 함수 호출을 모킹하고 검증합니다.

test('mock 함수 호출 테스트', () => {
  const mockFunction = jest.fn(() => 'Hello');
  mockFunction();
  expect(mockFunction).toHaveBeenCalled();
  expect(mockFunction).toHaveBeenCalledTimes(1);  // 호출 횟수 검증
  expect(mockFunction).toReturnWith('Hello');     // 반환값 검증
});

test('더미 함수 테스트', () => {
	const myMockFunction = jest.fn().mockImplementation((x) => x * 2);//더미함수정의

	expect(myMockFunction(2)).toBe(4);
	expect(myMockFunction(3)).toBe(6);

});
// 예제 객체와 메서드
const user = {
  setName(name) {
    console.log(`User's name is set to ${name}`);
    return name;
  },
};

test('setName 메서드 호출 여부를 스파이로 검증', () => {
  // user 객체의 setName 메서드를 스파이로 감시 시작!
  const spy = jest.spyOn(user, 'setName');

  // setName 메서드 호출
  user.setName('Alice');

  // setName 메서드가 'Alice'라는 인수로 호출되었는지 검증
  expect(spy).toHaveBeenCalledWith('Alice');

  // setName 메서드가 정확히 한 번 호출되었는지 검증
  expect(spy).toHaveBeenCalledTimes(1);

  // 원래 메서드의 동작을 유지하므로 반환값도 확인 가능
  expect(spy).toHaveReturnedWith('Alice');

  // 스파이한 메서드를 원래 상태로 복구
  spy.mockRestore();
});

3. 타이머 Mocking

setTimeout, setInterval 같은 타이머 함수의 동작을 모킹하여 테스트합니다.

이러한 타이머 관련 web api들은 실시간 시간 경과에 의존성을 가지기 때문에, 시간 경과를 제어하며 테스트해야 한다.

jest.useFakeTimers();

test('타이머 호출 테스트', () => {
  const callback = jest.fn();

  // 1초 후에 callback 함수가 호출되도록 설정
  setTimeout(callback, 1000);
  
  // 아직 시간이 지나지 않았으므로 callback이 호출되지 않음
  expect(callback).not.toHaveBeenCalled();
  
  // 1000ms 경과 시킴
  jest.advanceTimersByTime(1000);

  // 이제 callback이 한 번 호출되었는지 검증
  expect(callback).toHaveBeenCalled();
  expect(callback).toHaveBeenCalledTimes(1);
  
  jest.clearAllTimers(); // 타이머 상태 초기화
});

test('setInterval을 사용한 타이머 mock 테스트', () => {
  const callback = jest.fn();

  // 500ms마다 callback 함수 호출
  setInterval(callback, 500);
  
  // 1000ms가 경과한 상태에서 callback이 두 번 호출되었는지 확인
  jest.advanceTimersByTime(1000);
  expect(callback).toHaveBeenCalledTimes(2);
  
  jest.clearAllTimers(); // 타이머 상태 초기화
});
반응형

'Engineering > etc.' 카테고리의 다른 글

px, em, rem 차이를 알아보자  (2) 2022.12.18
0316_Web page를 꾸미는 역할, CSS  (0) 2020.03.16
0315_First step to Web, Summary about HTML  (0) 2020.03.16