[Jest] 목 함수

@1000peach2022. 06. 08  -  ☕️ 5 min read
[Jest] 목 함수

1. 목 함수 (Mock Function)

목 함수란 실제가 아닌 테스트를 위해 만든 모형 함수이다. 특정 기능에 집중하는 목 함수를 적절히 활용하여 효율적인 테스트 코드를 작성할 수 있다.

예를 들어 유저 DB에서 user list를 select 하는 작업 구현 시 작성해야 할 코드가 많아지며(테스트 코드보다 많아질 수도) 네트워크, DB 상태 등 외부 영향을 받기도 한다.

이를 목 함수로 테스트 한다면 같은 코드는 동일한 결과를 내는 것이 중요하다.


2. 사용 방법

2-1. 목 함수 생성

목 함수를 생성하고 프로퍼티를 알아본다.

const mockFn = jest.fn() // 목 함수 생성 mockFn() // 목 함수 호출 mockFn(1) // 인수 넘기기 test('함수는 두 번 호출 된다.', () => { console.log(mockFn.mock.calls) expect(mockFn.mock.calls.length).toBe(2) // calls.length === 호출된 횟수 }) test('두 번쨰로 호출된 함수에 전달된 첫 번째 인수는 1이다.', () => { expect(mockFn.mock.calls[1][0]).toBe(1) })
  • 생성된 목 함수에는 mock 프로퍼티가 있고 calls라는 배열을 갖고있다.

    • mock : 호출되었던 프로퍼티들이 저장

    • calls : 함수가 몇번 호출됐는 지, 호출될 때 전달된 인수는 무엇인 지 저장 -> 전달된 인수의 값이 배열로 들어있음

      calls console.log 결과

      목 함수 calls

2-2. 목 함수로 간단한 테스트

빠르고 간단한 테스트를 위해 목 함수를 활용한다.

인수로 받은 배열을 반복하면서 1씩 증가한 값을 콜백으로 넘겨주는 forEachAdd 함수를 테스트한다.
콜백 함수를 새로 만들어서 전달하지 않아도 목 함수를 이용해서 forEachAdd1 함수가 잘 동작하는 것을 확인할 수 있다.

const mockFn = jest.fn() const forEachAdd1 = (arr: number[]) => { arr.forEach(num => mockFn(num + 1)) } forEachAdd1([10, 20, 30]) test('함수는 3번 호출 된다.', () => { expect(mockFn.mock.calls.length).toBe(3) }) test('전달된 값은 11, 21, 31이다.', () => { expect(mockFn.mock.calls[0][0]).toBe(11) expect(mockFn.mock.calls[1][0]).toBe(21) expect(mockFn.mock.calls[2][0]).toBe(31) })

2-3. 값을 리턴하는 목 함수

값을 리턴하는 목 함수를 작성한다.

const mockFn = jest.fn(num => num + 1) mockFn(10) mockFn(20) mockFn(30) test('함수는 3번 호출 된다.', () => { console.log(mockFn.mock.results) // [{ type: 'return', value: 리턴값 }] expect(mockFn.mock.calls.length).toBe(3) }) test('10에서 1 증가한 값이 반환된다.', () => { expect(mockFn.mock.results[0].value).toBe(11) }) test('20에서 1 증가한 값이 반환된다.', () => { expect(mockFn.mock.results[1].value).toBe(21) })
  • results : 목 함수에서 리턴된 값이 배열로 들어있다.

    console.log 결과

    목 함수 results

  • 실행할 때마다 다른 값을 리턴하려면 mockFn.mockReturnValue을 사용한다.

    • 중간에 값을 바꾸려면 mockFn.mockReturnValue 사용

2-4. 비동기 목 함수

mockResolvedValue를 사용하여 비동기 목 함수를 작성할 수 있다.

const mockFn = jest.fn() mockFn.mockResolvedValue({ name: 'Mike' }) test('받아온 이름은 Mike', () => { mockFn().then(res => { expect(res.name).toBe('Mike') }) })

2-5. 외부 코드 활용 테스트

DB에 유저를 생성하는 외부 코드를 테스트한다고 생각해보자. 테스트마다 실제 유저되면 곤란하고 다시 삭제하는 것도 번거롭다.
이럴 때 jest.mock()으로 모듈을 모킹한다.

fn.ts

const fn = { // 실제로 유저 DB에 유저를 생성해주는 함수라고 가정 createUser: (name: string) => { console.log('실제로 사용자가 생성되었습니다.') return { name, } }, }

fn.test.ts

import fn from './fn' //////////////////////////// 모듈 모킹 코드 시작 jest.mock('./fn') // fn을 모킹 모듈로 만듦 // fn.createUser를 사용하고, {name: 'Mike'} 객체를 리턴하도록 설정 (fn.createUser as jest.MockedFunction<typeof fn.createUser>).mockReturnValue({ name: 'Mike', }) //////////////////////////// 모듈 모킹 코드 끝 // 테스트 성공 test('유저를 생성한다', () => { const user = fn.createUser('Mike') expect(user.name).toBe('Mike') })
  • 모듈 모킹 코드로 인해 실제 fn.createUser는 호출되지 않고(로그가 찍히지 않음), mockReturnValue를 객체를 리턴하는 목 함수가 동작한다.
실행하면 테스트는 성공하고 로그는 보이지 않는다. === 실제 유저가 생성되지 않음 === 실제 함수가 동작하지 않음

목 함수 동작

모듈 모킹 코드 주석을 해제하면 fn.createUser의 로그가 찍힌다. === 실제 함수가 동작함

실제 함수 동작


3. 유용한 매처

아래 toBeCalled, toBeCalledTimes, toBeCalledWith, lastCalledWith 매처를 활용한 테스트는 모두 성공한다.

const mockFn = jest.fn(); mockFn(10, 20); mockFn(); mockFn(30, 40); test("한번 이상 호출 되었나?", () => { expect(mockFn).toBeCalled(); // 한번이라도 호출 됐는 지 }); test("정확히 세번 호출 되었나?", () => { expect(mockFn).toBeCalledTimes(3); // 정확한 호출 횟수 }); test("10이랑 20 전달받은 함수가 있는가?", () => { expect(mockFn).toBeCalledWith(10, 20); // 인수로 어떤 값들을 받았는 지 체크 --> 30, 40으로 변경해도 성공 }); test("마지막 함수는 30이랑 40을 받았는가?", () => { expect(mockFn).lastCalledWith(30, 40); // 마지막으로 실행되는 함수의 인수만 체크 --> 10, 20으로 변경하면 실패 }); // 10, 20으로 바꾸면 실패

🔗 참고

이전 게시글

[Jest] 테스트 전후 작업

다음 게시글

[yarn] Yarn Berry 알아가기

profile

@1000peach

    GitHubGmailPortfolio
© 2022 1000peach, Powered By Gatsby.