My Dream Being Visualized

Jest를 활용한 TDD 단위 테스트 - 6편 본문

Backend/TDD

Jest를 활용한 TDD 단위 테스트 - 6편

마틴킴 2021. 12. 15. 07:20
728x90

 개인 공부를 위한 공간입니다. 틀린 부분 지적해주시면 감사하겠습니다 (_ _)

 

지난 시간에, Setup and Teardown에 대해서 알아보았다.

 

이번 시간엔, Mock Functions이라는 주제로 넘어가보겠다.

https://jestjs.io/docs/mock-functions

 

 

Mock Functions

Mock functions는 함수의 실제 구현부를 지우고, 함수 호출 및 해당 호출에 전달된 파라미터를 캡쳐하고, new로 인스턴스화 할 때 생성자 함수의 인스턴스를 캡쳐하고, 반환 값의 테스트 시간을 구성할 수 있어 코드간의 연결을 테스트 할 수 있다.

아니 이게 무슨 말이냐면..

Mock 함수(모의 함수=가짜로 만든 함수)는 실제 함수 내에 로직에 해당하는 부분들을 적지 않아도 되며,
함수 호출도 할 수 있고 호출에 전달된 파라미터들도 잡아낼 수 있으며,
new로 인스턴스를 만들 때, 생성자 함수의 인스턴스를 잡아낼 수 있으며,
반환할 때의 시간을 조정할 수 있어서 코드간의 연결을 테스트 할 수 있다!
// 잠깐! 생성자 함수란?
// 자바스크립트에서는 클래스가 없지만 프로퍼티가 똑같은 객체를 만들 수 있다.
// 객체를 생성하고, new 연산자를 이용해 새로운 객체를 생성하는 함수를 생성자 함수라고 한다.

// 코드가 더 이해가 빠를 것 같아 코드를 남긴다.

// 생성자 함수
function Book(title, price) {
  this.title = title;
  this.price = price;
}

// new로 만든 생성자 함수의 인스턴스
var book1 = new Book("부자아빠 가난한아빠", 10000)

함수를 mocking하는데엔 2가지 방법이 있다.

  1. 테스트 코드에서 mock 함수를 생성하거나
  2. 실제로 mock 함수를 작성하거나

 

들어가기에 앞서

jest.fn(implementation)
> mock 함수를 생성할 때 쓴다. optional하게 implementation을 넣을 수도 있다.
const mockFn = jest.fn();
mockFn();
expect(mockFn).toHaveBeenCalled();

// Optional한 implementation도 넣었다.
const returnsTrue = jest.fn(() => true);
console.log(returnsTrue()); // true;

 

Using a mock function

callback함수를 실행하는 forEach 함수를 실행하는 테스트를 해보자.

아래 함수를 테스트하기 위해서, mock 함수를 만들고 callback이 예상대로 불러졌는지 mock의 상태를 체크 해보자.

function forEach(items, callback) {
  for (let index = 0; index < items.length; index++) {
    callback(items[index]);
  }
}

const mockCallback = jest.fn(x => 42 + x);
forEach([0, 1], mockCallback);

/*
  mockFn.mock.calls: mock함수에 호출된 횟수 및 arguments를 포함하는 array
  mockFn.mock.results: { type: '', value: '' } 형태의 array
*/
// mock 함수는 2번 호출된다. [0, 1]
expect(mockCallback.mock.calls.length).toBe(2);

// 첫 번째 함수 호출의 argument는 0이다.
expect(mockCallback.mock.calls[0][0]).toBe(0);

// 두 번째 함수 호출의 argument는 1이다.
expect(mockCallback.mock.calls[1][0]).toBe(1);

// 첫 번째 함수 호출의 리턴 값은 42이다.
expect(mockCallback.mock.results[0].value).toBe(42);
위에서 언급했던,
함수 호출을 몇번 하는지, 함수 호출 시 전달되는 파라미터들도 잡아낼 수 있으며, 리턴값도 알 수 있다!

.mock property

모든 mock 함수는 .mock 프로퍼티를 가지고 있는데, 어떻게 함수가 호출이 됐고 함수가 어떤 값을 리턴하는지에 대한 데이터가 저장되는지를 보여준다. .mock 프로퍼티는 또한 매 호출마다 this의 값을 트래킹하고 검사할 수도 있다.

어떻게 각 함수들이 호출되었고, 생성되었고, 어떤 값을 리턴하는지 확인하기 위해서 테스트에서 유용하게 쓰인다.

// mock 함수를 만든다.
const myMock = jest.fn();

// 생성자 함수의 인스턴스를 만든다. a, b
const a = new myMock();
const b = {};
const bound = myMock.bind(b);
bound();

console.log(myMock.mock.instances);
// > [ <a>, <b> ]

 

Mock Return Values

mock 함수는 테스트 동안 코드 내에 테스트 값을 넣을 수도 있다.

실제 내부 코드를 수정할 필요 없이, mockReturnValueOnce를 통해 지정할 수도 있다.

 

const filterTestFn = jest.fn();

// 첫 번째 호출 시 true를, 두 번째 호출 시 false를, 세 번째 호출부터는 true를 return!
filterTestFn.mockReturnValueOnce(true).mockReturnValueOnce(false).mockReturnValue(true);

const result = [11, 12, 13, 14].filter(num => filterTestFn(num));

console.log(result);
// > [11, 13, 14]
console.log(filterTestFn.mock.calls[0][0]); // 11
console.log(filterTestFn.mock.calls[1][0]); // 12
console.log(filterTestFn.mock.calls[2][0]); // 13
console.log(filterTestFn.mock.calls[3][0]); // 14

 

Mock 함수를 기본적으로 만드는 방법을 알아보았다.
다음 시간에는, Mock functions편에 이어 Axios를 어떻게 mock 함수로 만드는지에 대해서 알아볼 예정이다.
하나씩 배워가는 재미!