Jest의 Mock 함수를 구현하는 3가지 방법에 대한 설명을 번역한 글입니다. 어느 상황에 어떤 Mock 함수를 써야할지 몰라서 헤매던 와중에 발견했습니다. 원문에 달려있는 공식문서보다 이해하기 잘 쓰여진 글이다
라는 댓글에 백번 공감하며 번역해보았습니다.
원문은 https://medium.com/@rickhanlonii/understanding-jest-mocks-f0046c68e53c 에서 확인하실 수 있습니다.
틀린 부분은 편하게 댓글 달아주시면 감사하겠습니다~!
Mocking은 테스트를 독립시키기 위해 의존성을 개발자가 컨트롤하고 검사할 수 있는 오브젝트로 변환하는 테크닉입니다. 의존성은 무엇이든 될 수 있지만, 일반적으로 import 하는 모듈입니다.
자바스크립트에는 testdouble과 sinon처럼 훌륭한 mocking 라이브러리가 있고, Jest는 기본적으로 제공하는 기능입니다.
최근에 저는 Jest의 이슈 트래커를 돕기위해 Collaborator로서 Jest팀에 참여했습니다. 거기서 많은 이슈들이 Jest에서 어떻게 mocking 하는지에 관한 질문이라는 것을 깨닫고, 이 것들을 한번에 설명하는 가이드를 작성하기로 했습니다.
우리가 Jest에서 Mocking을 이야기할 때, 일반적으로 의존성을 Mock Function으로 대체하는 것에 대해 말합니다. 이 글에서 Mock 함수에 대해 리뷰해보고, 의존성을 대체하는 여러가지 방법으로 deep dive 해보겠습니다.
The Mock Function
Mocking의 목적은 우리가 컨트롤할 수 없는 무엇인가를 대체하는 것이기 때문에, 우리가 대체하는 것이 필요로하는 모든 기능을 갖고 있는게 중요합니다.
Mock 함수는 다음 기능을 제공합니다.
- 함수호출 Capture
- Return Value 설정
- 구현 변경하기
Mock 함수 인스턴스를 만드는 가장 간단한 방법은 jest.fn()
을 쓰는 것입니다.
이 것과 Jest Expect를 쓰면, 함수호출을 Capture해서 쉽게 테스트할 수 있습니다.
그리고 Return Value, 구현, Promise Resolution을 바꿀 수도 있습니다.
Mock 함수가 무엇이고 이 것으로 무엇을 할 수 있는지 알아봤습니다. 이제 어떻게 사용할지 알아봅시다
의존성 주입
Mock 함수를 사용하는 일반적인 방법 중 하나는 테스트하려는 함수로 arguments를 직접 전달하는 방식입니다. 이 것은 테스트를 실행시키고, Mock 함수가 어떤 arguments와 어떻게 실행됐는지 assert구문으로 확인해 볼 수 있습니다.
이 전략은 견고한 테스트를 만들지만 테스트코드가 의존성주입을 허용하도록 요구합니다. 종종 그럴 수 없는 경우에, 우리는 실제로 존재하는 모듈과 함수를 Mocking 해야 합니다.
모듈과 함수를 Mocking하기
Jest에서 모듈과 함수를 Mocking 하는 3가지 방법이 있습니다.
jest.fn
: Mock a functionjest.mock
: Mock a modulejest.spyOn
: Spy or mock a function
이 것들은 각각의 방식으로 Mock 함수를 만드는데, 어떻게 동작하는지 설명을 하기 위해 다음과 같은 폴더구조로 만들어 보겠습니다.
이 설정에서는 math.js
함수를 실제로 호출하지 않고 app.js
를 테스트하면서, 함수가 예상대로 호출되는지 확인하기 위해 Spy를 하는 것이 일반적입니다. 예시들은 진부하지만 math.js
의 함수들이 복잡한 계산을 하거나 개발자가 피하고싶은 IO를 만드는 요청이라고 상상해주세요.
jest.fn으로 Mocking 하기
가장 기본적인 전략은 함수를 Mock 함수로 재할당하는 것입니다. 재할당된 함수가 쓰이는 어디서든지 Mock 함수가 원래의 함수 대신 호출될 것입니다.
이렇게 Mocking 하는 방식은 몇 가지 이유로 덜 쓰입니다.
jest.mock
은 자동적으로 모듈의 모든 함수를 Mocking 해줍니다.jest.spyOn
도 마찬가지로 모든 함수를 Mocking 해주면서 원래의 함수를 다시 복원할 수도 있습니다.
jest.mock으로 Mocking 하기
좀 더 일반적인 접근법은 자동적으로 모듈이 exports하는 모든 것들을 Mocking 해주는 jest.mock
을 쓰는 것입니다. 따라서 jest.mock('./math.js')
를 해주면 본질적으로 math.js
를 다음처럼 설정하는 것입니다.
여기서부터 모듈이 exports 하는 모든 것들에 Mock 함수 기능을 쓸 수 있습니다.
이것은 가장 쉽고 일반적인 Mocking 방법입니다. (Jest의 automock: true
설정 방식이기도 합니다)
이 전략의 유일한 단점은 모듈의 원래 구현에 접근하기 어렵다는 것입니다. 이런 경우를 대비해 spyOn
이 있습니다.
jest.spyOn으로 Spy 혹은 Mocking하기
때로 우리는 메소드가 실행되는 것을 지켜보길 원할뿐만 아니라, 기존의 구현은 보존하길 바랍니다. 구현을 Mocking하고 차후에 테스트구문에서 원본을 복원할 수 있습니다.
이 경우에 jest.spyOn
을 쓸 수 있습니다.
단순히 math 함수에 “Spy”를 호출하고 원본 구현은 그대로 둘 수 있습니다.
이것은 실제로 함수를 대체하지 않고, 특정한 사이드 이펙트가 발생하는지 테스트하는 몇몇 시나리오에 유용합니다.
함수를 Mocking하고 다시 원래 구현을 복원할 수도 있습니다.
Jest는 각각의 테스트 파일이 샌드박스화 되어 있기 때문에, afterAll
훅을 불필요하게 사용하지 않도록 하는 경우에 유용합니다.
jest.spyOn
는 기본적으로 jest.fn()
의 사용에 대한 Sugar(일반적으로 말하는 Syntactic Sugar를 말하는 것 같습니다: 역자 주)라는 것이 키포인트 입니다. 우리는 기존의 구현을 저장하고, Mocking 했다가, 기존 구현을 재할당하는 방식으로 똑같은 목표를 달성할 수 있습니다.
이 것이 실제로 jest.spyOn
이 구현된 방식입니다.
결론
이 글에서 우리는 Mock 함수가 무엇인지와 모듈과 함수 호출을 트래킹하고, 구현과 return value를 바꾸는 방법을 배웠습니다.
저는 여러분이 Jest Mock을 쉽게 이해하고 고통없이 테스트를 작성하는데 더 많은 시간을 쓸 수 있도록 돕기를 바랍니다. Mocking에 대한 더 많은 정보와 best practice들은 Justin Searls에 의해 Don’t Mock Me 라고 이름 붙여진 발표와 700장이 넘는 슬라이드를 확인해보세요.
트위터와 스택오버플로우, 디스코드 채널로 무엇이든 물어보세요.