3 - 테스트 더블

업데이트: Link

3장 테스트 더블

Written by Alex Jover Morales

Test doubles, spies, and mocking, 모두 흥미롭게 들리지만 실제로 의미하는 바는 무엇입니까? 정말로 내 코드를 감시해야 합니까, 아니면 mocking 해야 합니까? 알아 보자!

의존성 다루기

애플리케이션을 빌드할 때 코드를 구조화하기 시작하여 애플리케이션 전체에서 가져오고 재사용하는 다른 구성 요소와 모듈로 코드를 분리합니다.

모듈이 다른 모듈을 가져올 때 마지막 모듈이 첫 번째 모듈의 종속성이 됩니다. 예를 들어, 학생 ID의 존재를 확인 하는 함수 validateStudent 가 있는 student-utils.js 모듈 이 있다고 상상해 보십시오.

// student-utils.js
export const validateStudent = student => {
  return Boolean(student.id && student.id > 0);
}

그리고 다른 모듈 student.js 에서 사용합니다 .

// student.js
import { validateStudent } from "./student-utils";

export default function createStudent(id, name) {
  const student = { id, name };
  if (!validateStudent(student)) {
    throw new Error("Invalid student: it doesn't have an id");
  }
  return student;
}

그러면 student-utilsstudent의 종속성이 됩니다. 즉, student를 사용할 때마다 암시적으로 student-utils도 함께 사용됩니다.

이 종속성은 논리적으로 보이고 무해해 보일 수 있지만 종속성은 종종 모듈을 테스트하기 어렵게 만들 수 있습니다. 질문은…

종속성이 테스트를 어렵게 만드는 경우는 언제입니까?

단순: 종속성에 부작용이 있는 경우. 부작용이란 API, 데이터베이스, 일종의 상태 등과 같은 외부적인 것을 호출하는 함수를 의미합니다.

마지막 예제는 validateStudent가 학생을 받아 ID에 대해 몇 가지 검사를 수행하기 때문에 간단하고 순수합니다. 따라서 부작용이 없습니다.

그러나 API를 호출하는 fetchStudent 함수가 있다고 상상해보십시오.

// student-utils.js
const baseUrl = "https://jsonplaceholder.typicode.com/users/";

export const fetchStudent = async id => {
  const student = await fetch(baseUrl + id);
  return student.json();
};

단위 또는 통합 테스트를 수행할 때 추가 종속성을 피하면서 단위 또는 시스템의 일부를 테스트하려고 합니다. fetchStudent 함수는 외부 호출에 의존하기 때문에 부작용이 있으므로 테스트가 중단되거나 일관성이 없습니다.

테스트 더블이 작동하는 곳입니다.

테스트 더블이란?

스턴트 더블을 생각해 보세요. 어떤 위험한 스턴트를 수행해야 할 때 실제 배우를 대신하는 사람입니다.

테스트 더블은 스턴트 더블과 관련이 있습니다. 테스트를 더 쉽게 만들고 종속성을 분리하기 위해 원래 ~~test~~ 항목을 대체하는 개체입니다.

케이스에 따라 단위 테스트, 통합 테스트에 자주 사용됩니다. 우리가 달성하고자 하는 것은 부작용을 수행하는 종속성동일한 인터페이스를 공유하는 테스트로 이중으로 대체하는 것입니다.

다양한 유형의 테스트 더블이 있으며, 각각은 spies, stubs, mocks 등 목적이 다릅니다.

Spies

스파이는 테스트 항목을 관찰하고 무언가가 호출된 횟수를 세는 데 사용되지만 구현을 변경하지는 않습니다.

부작용이 없어 의존성을 교체할 필요가 없는 경우에 일반적으로 사용됩니다.

이전 예제의 validateStudent 함수의 경우가 완벽합니다. 구현을 대체하는 것은 의미가 없습니다. 학생에게서 테스트하고 싶은 것은 학생이 생성될 때 유효성이 검사된다는 것입니다.

이제 학생이 올바르게 생성되었는지 확인하기 위해 어떤 종류의 스파이도 사용하지 않고 학생 생성의 첫 번째 테스트를 구현해 보겠습니다.

import createStudent from "./student";

describe("Student", () => {
  it("can create a student passing an id and name", () => {
    const student = createStudent(2);
    expect(student.id).toBe(2);
  });
});

테스트는 기본적으로 사용자가 생성되었고 올바른 ID를 가지고 있다고 주장합니다. 그것은 올바른 경우이지만 잘못된 테스트의 경우 예외가 트리거되었는지 확인해야 합니다.

Jest는 예외를 위한 toThrow 매처와 함께 제공되며 이는 우리의 사용 사례에 적합합니다. 특정 오류를 주장하는 인수도 허용합니다.

it("throws an error if id is not passed", () => {
  expect(() => createStudent()).toThrow();

  // You can be more specific
  expect(() => createStudent()).toThrow(Error);
  const msg = "Invalid student: it doesn't have an id";
  expect(() => createStudent()).toThrow(msg);
});

아마도 당신은 이 테스트를 통해 우리가 validateStudent 함수를 암시적으로 테스트하고 있다는 것을 깨달았을 것입니다. 예외가 발생하면 학생은 유효하고 다른 경우에는 유효하지 않습니다. validateStudent 함수를 별도로 테스트할 수도 있지만, createStudent가 이미 테스트했기 때문에 필요하지 않다고 말하는 경우 중 하나입니다.

그러나 createStudent 함수가 아래에서 validateStudent를 호출하는지 명시적으로 확인하려는 경우 jest.spyOn을 사용하여 validateStudent에 대한 스파이를 만들 수 있습니다.

spyOn 함수에는 spyOn(object, propName) 서명이 있으므로 validateStudent 속성에서 모든 학생 유틸리티와 스파이를 가져와야 합니다.

import createStudent from "./student";
import * as utils from "./student-utils";

describe("Student", () => {
  // ...
  it("calls the validateStudent function", () => {
    jest.spyOn(utils, "validateStudent");
    createStudent(1, "Aaron");
    expect(utils.validateStudent).toBeCalled();
  });
});

나는 utils.validateStudent에서 toBeCalled 매처를 사용했는데, 이것은 함수가 호출되었다고 주장합니다. 호출된 횟수 또는 매개변수를 정의하여 보다 구체적으로 지정할 수 있습니다.

expect(utils.validateStudent).toBeCalledWith({ id: 1, name: "Aaron" });
expect(utils.validateStudent).toHaveBeenCalledTimes(1);

우리는 여전히 뭔가를 놓치고 있습니다. 동일한 함수를 호출하는 다른 테스트를 만들고 각 테스트에서 정확히 한 번만 호출되었는지 확인하면 어떻게 될까요?

describe("Student", () => {
  it("calls the validateStudent function", () => {
    const spy = jest.spyOn(utils, "validateStudent");
    createStudent(1, "Aaron");
    expect(spy).toHaveBeenCalledTimes(1);
  });

  it("calls the validateStudent function", () => {
    const spy = jest.spyOn(utils, "validateStudent");
    createStudent(2, "Peep");
    expect(spy).toHaveBeenCalledTimes(1);
  });
});

Reset mock state

이 테스트가 통과할 것으로 예상할 수 있지만 두 번째 테스트는 다음 오류와 함께 실패합니다.

Expected mock function to have been called one time, but it was called two times.

이는 첫 번째 테스트에서 이미 스파이를 설정하고 있고 두 번째 테스트에서 jest.spyOn을 다시 호출하더라도 validateStudent 함수가 이미 스파이되고 호출 카운터가 재설정되지 않아 호출이 누적되기 때문입니다.

이러한 이유로 각 테스트 후에 스파이 카운터를 재설정하는 것이 매우 중요합니다. Jest는 이를 위해 세 가지 기능을 제공합니다.

  • mockClear: 카운터만 재설정
  • mockReset: 구현을 포함하여 모의를 원래 상태로 재설정합니다.
  • mockRestore: mockReset과 유사하지만 모의 함수를 제거합니다.

Jest는 모의, 스텁 및 스파이를 모의 기능으로 일반화한다는 점을 명심하십시오.

우리의 경우 mockClearmockRestore 모두 작동합니다:

it("calls the validateStudent function", () => {
  const spy = jest.spyOn(utils, "validateStudent");
  createStudent(1, "Aaron");
  expect(spy).toHaveBeenCalledTimes(1);
  spy.mockClear();
});

it("calls the validateStudent2 function", () => {
  const spy = jest.spyOn(utils, "validateStudent");
  createStudent(2, "Peep");
  expect(spy).toHaveBeenCalledTimes(1);
  spy.mockClear();
});

Jest Hooks

모의 함수를 만들고 지우는 것이 매우 반복적일 수 있음을 알 수 있습니다. 운 좋게도 Jest에는 다양한 지점(beforeEach, afterEach, beforeAll, afterAll)에서 모든 작업을 수행하는 데 사용할 수 있는 후크가 있습니다. 범위는 해당 위치의 설명 기능입니다.

우리의 경우 afterEach를 사용하여 각 테스트 후에 스파이를 정리할 수 있습니다.

import createStudent from "./student";
import * as utils from "./student-utils";

describe("Student", () => {
  const spy = jest.spyOn(utils, "validateStudent");

  afterEach(() => {
    spy.mockClear();
  });

  it("calls the validateStudent function", () => {
    createStudent(1, "Aaron");
    expect(spy).toHaveBeenCalledTimes(1);
  });

  it("calls the validateStudent2 function", () => {
    createStudent(2, "Peep");
    expect(spy).toHaveBeenCalledTimes(1);
  });
});

그리고 보시다시피, 우리는 반복을 피합니다.

이 경우 beforeAll 후크는 스파이를 설정하는 데 필요하지 않지만 그럴 수 있는 더 복잡한 경우가 있습니다. 예를 들어 데이터베이스 정리와 같은 테스트를 실행하기 전에 비동기 작업을 수행해야 한다고 가정해 보겠습니다.

이러한 경우 후크와 it 테스트 함수는 모두 약속을 반환할 수 있으며 해결될 때까지 기다립니다. 예를 들어:

beforeAll(() => {
  return Promise.resolve("Dummy example");
});

더 멋지고 동기화와 유사한 구문을 위해 async/await 구문을 사용할 수도 있습니다. 다음 예는 이전과 동일합니다.

beforeAll(async () => {
  await Promise.resolve("Dummy example");
});

Mocks and Stubs

모의 객체와 스텁의 차이점은 논란의 여지가 있으며 명확하지 않습니다. 둘 다 동일한 것을 달성합니다. 테스트의 종속성을 분리하기 위해 실제 구현을 가짜 구현으로 바꾸는 것입니다.

일부는 스텁 구현이 정적이지만 모의는 테스트별로 동적이라고 말합니다. 어쨌든, 지금부터 우리는 그것들을 모두 mock이라고 부를 것입니다.

모의에 들어가기 전에 student-utils.jsfetchStudent 함수를 사용하는 가져오기 함수를 student.js에 구현해 보겠습니다.

import { validateStudent, fetchStudent } from "./student-utils";

export default function createStudent(id, name) {
  const student = { id, name };
  if (!validateStudent(student)) {
    throw new Error("Invalid student: it doesn't have an id");
  }
  // Add the fetch function before returning the object
  student.fetch = () => fetchStudent(student.id);
  return student;
}

먼저 종속성과 부작용의 문제를 확인하기 위해 조롱 없이 student.fetch 함수를 테스트할 것입니다. 그런 다음 이러한 문제를 피하기 위해 종속성 주입 기술과 mocking을 살펴보겠습니다.

fetch 함수를 호출하고 일부 데이터를 주장하는 테스트를 student.test.js에 추가하여 시작합니다.

describe("student.fetch", () => {
  it("returns data", async () => {
    const student = createStudent(1);
    const data = await student.fetch();
    expect(data.username).toBe("Bret");
  });
});

문제는 테스트를 실행하면 student-utils.js에서 가져오기 기능을 찾지 못하기 때문에 실패한다는 것입니다.

전제 조건: Jest 설정 및 폴리필 가져오기

문제는 Jest가 브라우저를 에뮬레이트하는 노드 환경인 JSDom 위에서 실행된다는 것입니다. Fetch API는 최신 브라우저에서 구현된 웹 표준이며 Jest에서는 사용할 수 없습니다.

작동하게 하려면 최신 브라우저에서 누락된 기능을 구현하는 라이브러리인 폴리필을 사용해야 합니다. Node.js와 브라우저 모두에서 작동하기 때문에 우리는 isomorphic-fetch를 사용할 것입니다. 다음을 설치하여 시작하십시오.

npm install --save-dev isomorphic-fetch

Jest는 기본적으로 작동하지만 많은 구성 옵션을 사용할 수 있습니다. 더 많은 유연성이 필요한 경우 “jest” 속성을 package.json에 추가하거나 jest.config.js 파일을 만들어 설정할 수 있습니다. 더 간단하고 추가 파일이 필요하지 않기 때문에 우리는 package.json 접근 방식을 사용할 것입니다.

모든 Jest 옵션 중에서 setupScripts를 사용하고 스크립트 경로를 전달할 수 있습니다. 이것은 테스트를 실행하기 전에 몇 가지 준비를 해야 하는 이와 같은 경우에 유용합니다. setupTests 파일을 전달하는 package.json에서 설정해 보겠습니다.

{
  // ...
  "jest": {
    "setupFiles": ["./setupTests"]
  }
}

그런 다음 프로젝트의 루트에 setupTests.js를 만들고 isomorphic-fetchglobal.fetch 속성에 할당합니다.

global.fetch = require("isomorphic-fetch");

참고:
global 은 Node.js 환경의 window와 동일합니다.

테스트를 다시 실행하면 더 이상 fetch 기능을 찾지 못해 충돌이 발생하지 않습니다.

왜 mocking 인가?

테스트로 돌아가서:

it("returns data", async () => {
  const student = createStudent(1);
  const data = await student.fetch();
  expect(data.username).toBe("Bret");
});

이 글을 쓰는 날까지 이 테스트는 통과하고 있습니다. 그러나 이 테스트가 잘못된 몇 가지 이유가 있습니다.

후드 아래에서 실제 가져오기 기능을 사용하는 fetchStudent 유틸리티를 사용합니다. 인터넷에 의존합니다. 서버의 응답은 완전히 예측할 수 없습니다. 사용자 이름은 언제든지 변경할 수 있습니다. 이러한 이유로 이 테스트는 전혀 신뢰할 수 없습니다. 그리고 가장 중요한 것은 테스트의 목적은 student.fetch가 호출되고 데이터를 반환하는지 확인하는 것이지만 다른 것은 신경 쓰지 않습니다. 이는 단위 테스트 시 대부분의 테스트에 적용됩니다. 추가 종속성을 피하면서 하나의 특정 사례를 테스트하는 데 집중해야 합니다.

의존성 주입

mocking 외에도 종속성을 처리하는 데 사용할 수 있는 다른 기술이 있습니다.

종속성 주입은 외부에서 개체에 종속성을 전달하도록 지시하는 디자인 패턴입니다. 그런 식으로 해당 개체에는 하드 코딩된 종속성이 없습니다. 그런 식으로 그 종속성은 “다른 누군가”가 해당 개체의 사용자라는 문제입니다.

예를 들어 이해하면 더 쉬울 수 있습니다. 현재 student.jsfetchStudent 함수에 대해 student-utils.js에 의존합니다. 예를 들어 createStudent 함수의 매개변수로 외부에서 fetchStudent 함수를 전달하면 더 이상 해당 종속성이 없습니다.

import { validateStudent } from "./student-utils";
export default function createStudent(id, name, fetchStudent) {
  const student = { id, name };
  if (!validateStudent(student)) {
    throw new Error("Invalid student: it doesn't have an id");
  }
  student.fetch = () => fetchStudent(student.id);
  return student;
}

보시다시피 createStudent의 3번째 매개변수에 제공하고 있습니다. validateStudent에도 동일하게 적용할 수 있지만 해당 기능은 문제가 되지 않으므로 무시할 수 있습니다.

의존성을 깨뜨렸으므로 이제 createStudent에 자체 함수를 전달하여 쉽게 테스트할 수 있습니다.

describe("student.fetch", () => {
  const fetchStudent = () => Promise.resolve({ username: "Bret" });

  it("returns data", async () => {
    const student = createStudent(1, "", fetchStudent);
    const data = await student.fetch();
    expect(data.username).toBe("Bret");
  });
});

이제 우리는 이 테스트를 신뢰할 수 있게 만들었습니다. 인터넷이나 외부 데이터 소스에 의존하지 않고 목표를 달성합니다. 그리고 가장 좋은 점은 조롱할 필요가 전혀 없고 기술만 있으면 된다는 것입니다.

의존성 주입의 단점은 더 많은 상용구가 필요하다는 것입니다. 이제 createStudent를 호출할 때마다 fetchStudent 함수를 전달해야 합니다. 종속성을 연결하는 추상화를 언제든지 만들 수 있습니다. 결국 이것은 기술이니, 상황에 도움이 될 때 사용하세요.

Mock 사용하기

Jest에는 매우 강력한 기능인 모의 기능이 있습니다. 생성하려면 jest.fn()에서 결과를 얻는 것만 큼 간단합니다.

const myMock = jest.fn();

스파이로부터 동일한 callable API를 공유하므로 호출되었는지 확인할 수 있습니다.

myMock("hiyaa");
expect(myMock).toBeCalledWith("hiyaa");
myMock.mockClear();

또한 jest.fn에 매개변수로 함수를 전달하여 구현을 정의할 수 있습니다.

const myMock = jest.fn(() => 12);
expect(myMock()).toBe(12);

더 세분화된 제어가 필요한 경우 mockImplementationOncemockImplementation 함수를 사용할 수 있습니다. 첫 번째는 원 호출 구현을 정의하고 다른 하나는 기본 구현을 정의합니다.

const myMock = jest.fn()
  .mockImplementationOnce(() => 12)
  .mockImplementationOnce(() => "Caribu cola")
  .mockImplementation(() => "Miau");

expect(myMock()).toBe(12);
expect(myMock()).toBe("Caribu cola");

// From here, it'll be always Miau
expect(myMock()).toBe("Miau"); 
expect(myMock()).toBe("Miau");

모의 함수를 사용하여 fetchStudent의 이전 예제를 구현할 수 있습니다. 호출을 확인할 수 있습니다.

const fetchStudent = jest.fn(() => Promise.resolve({ username: "Bret" }));

it("returns data", async () => {
    const student = createStudent(1, "", fetchStudent);
    const data = await student.fetch();
    expect(data.username).toBe("Bret");
    expect(fetchStudent).toBeCalledWith(1);
});

모의 함수가 매우 강력하다는 것을 알 수 있지만 종속성 주입을 사용하지 않고 모듈을 모의하는 방법을 여전히 모릅니다.

Mocking modules

의존성 주입을 사용하기 전에 코드로 시간을 되돌려 보겠습니다.

import { validateStudent, fetchStudent } from "./student-utils";

export default function createStudent(id, name) {
  // ...
  student.fetch = () => fetchStudent(student.id);
}

Jest는 종속성 주입을 사용하지 않고 종속성을 제거하는 좋은 방법인 모의 모듈을 제공합니다. 모의할 모듈 경로를 첫 번째 인수로 사용하는 jest.mock 함수를 사용하여 모듈을 모의할 수 있습니다.

jest.mock은 전체 모듈을 mock하고 fetchStudent 함수를 mock하고 싶기 때문에 이를 리팩토링하고 student-service.js 모듈로 옮겨야 합니다. HTTP 동사 규칙을 준수하도록 get 이름을 변경하여 객체를 반환합니다.

// student-service.js
const baseUrl = "https://jsonplaceholder.typicode.com/users/";

export default {
  async get(id) {
    const result = await fetch(baseUrl + id);
    return result.json();
  }
};

그리고 그에 따라 student.js를 업데이트합니다.

import { validateStudent } from "./student-utils";
import studentService from "./student-service";

export default function createStudent(id, name) {
  // ...
  student.fetch = () => studentService.get(student.id);
}

테스트는 리팩터링 이전과 동일한 방식으로 작동해야 합니다.

이제 students-service 모듈을 mock하기 위해 jest.mock을 사용할 수 있습니다.

// student.test.js
jest.mock("./student-service");
// ...
describe("student.fetch", () => {
  it("returns data", async () => {
    const student = createStudent(1, "");
    const data = await student.fetch();
    expect(data.username).toBe("Brat");
  });
});

jest.mock은 파일 상단에 호이스트되므로 혼동을 피하기 위해 항상 상단에 작성합시다.

마찬가지로 jest.mock은 자동으로 students-service 모듈에서 반환된 각 속성을 빈 jest.fn 구현으로 mock합니다. 즉, 그 시점부터 학생 서비스 구현은 다음과 같습니다.

{
  get: jest.fn()
}

이것이 바로 student.fetch()가 어떤 값도 반환하지 않기 때문에 테스트를 실행하면 실패하는 이유입니다. 그러나 우리는 students-service를 가져오고 get 호출을 확인할 수 있습니다. mock 함수의 시작입니다.

jest.mock("./student-service");
import studentService from "./student-service";
// ...
it("returns data", async () => {
  const student = createStudent(1, "");
  const data = await student.fetch();
  expect(studentService.get).toBeCalledWith(1);
});

더 세분화된 구현이 필요한 경우 jest.mock은 두 번째 인수로 해당 모듈의 사용자 지정 모의 구현을 갖는 팩토리 함수를 받을 수 있습니다. 예를 들어, 임의의 값으로 해석되는 promise를 반환하는 get 함수를 만들어 보겠습니다.

jest.mock("./student-service", () => ({
  get() {
    return Promise.resolve("Yay");
  }
}));

it("returns data", async () => {
  const student = createStudent(1, "");
  const data = await student.fetch();
  expect(data).toBe("Yay");
});

이 시점에서 모듈 모의를 적용하는 여러 가지 방법을 보았습니다. 모두 쉽게 코드 종속성을 제거할 수 있습니다.

상상할 수 있듯이 실제 앱에는 이러한 서비스가 많이 있으며 대부분의 경우 가져오기 기능 구현에 신경 쓰지 않고 호출되기를 원하지 않습니다.

더 쉽고 간결한 방법이 있다고 말하면 어떨까요?

Mock files

Jest는 다시 한 번 mock 기능이 돋보입니다. 모든 모듈의 mock 구현을 정의할 수 있는 mock 파일을 정의할 수 있습니다.

mock 파일은 규칙에 따라 작동합니다. mock 하려는 모듈과 동일한 수준에 *mocks******* 폴더를 생성하기만 하면 됩니다. 예를 들어, 모듈이 *src/student.js*에 있는 경우 *src/mocks/student.js*에서 생성해야 합니다. Jest는 jest.mock("/student.js")을 호출할 때 student 모듈에 mock 객체가 있음을 감지하고 적용됩니다.

이전 예제에서 mock 코드를 분리하고 필요한 모든 테스트에서 재사용하기 위해 mocks/student-service.js 파일을 만들 수 있습니다.

// __mocks__/student-service.js
export default {
  get: jest.fn(id => Promise.resolve(id))
};

그리고 테스트에서 사용자 정의 구현을 제거하세요.

// student.test.js
jest.mock("./student-service");
// ...
it("returns data", async () => {
  const student = createStudent(1, "");
  const data = await student.fetch();
  expect(data).toBe(1);
});

더 복잡하고 확장된 구현을 포함하는 많은 모듈이 있는 중형에서 대형 코드베이스에서 이는 모든 테스트에 대한 추가 설정을 피하고 테스트 및 소스 코드에서 mock 코드를 분리합니다.

우리는 이 예를 더 개선할 수 있습니다. 그것이 mock의 전부는 아니지만 소프트웨어 개발은 기술과 디자인 패턴에 관한 것임을 기억하십시오.

실제 응용 프로그램에서는 fetch function, axios 또는 사용하는 모든 것에 대해 고유한 API 클라이언트 추상화를 구현하는 것이 일반적입니다. 그러면 모든 테스트에 대해 해당 API 모듈을 자동으로 mock 할 수 있으므로 API를 호출하는 모든 서비스를 mock 할 필요가 없습니다.

api.js 파일을 만들어 예제를 시작하겠습니다.

// api.js
export default {
  async get(url) {
    const result = await fetch(url);
    return result.json();
  }
};

그런 다음 student-service에서 가져와서 사용합니다:

// student-service.js
import api from "./api";
const baseUrl = "https://jsonplaceholder.typicode.com/users/";

export default {
  get(id) {
    return api.get(baseUrl + id);
  }
};

그리고 여기 멋진 부분이 있습니다. 이전에 만든 setupTests.js 파일 에서 전역적으로 mock 할 수 있습니다. ./api.js 는 우리가 항상 mock 하기를 원하는 파일이기 때문에 완전히 의미가 있습니다.

// setupTests.js 
global.fetch = require("isomorphic-fetch");
jest.mock("./src/api");

그렇게 하면 더 이상 테스트에서 서비스를 mock 하거나 API 를 mcok 할 필요가 없어 더 깨끗하고 간결해집니다.

/ student.test.js
import createStudent from "./student";
import * as utils from "./student-utils";

describe("Student", () => {
  // ...
  describe("student.fetch", () => {
    it("returns data", async () => {
      const student = createStudent(1, "");
      const data = await student.fetch();
      expect(data).toBe("https://jsonplaceholder.typicode.com/users/1");
    });
  });
});

특정 테스트에 대한 구현을 계속 다시 작성할 수 있음을 명심하십시오. 따라서 다른 테스트에서 api의 사용자 정의 구현이 필요하지 않은 경우에도 여전히 수행할 수 있습니다.

// student.test.js
jest.mock("./student-service", () => ({
  get: jest.fn(() => "Something different meh :S")
}));

댓글남기기