해결된 질문
작성
·
600
1
mocking과 spy함수가 조금 헷갈립니다. 아래와 같이 정리하면 될까요?
- spy 함수 : 빈 함수인데, vitest에서 이 함수를 감지하고 있고 함수가 call 되었는지, 인자는 무엇이었는지 검증하는 가짜 함수.
- mocking : 종속성이 있는 라이브러리를 복사해두고, 그 중 사용해야 할 함수나 기능을 spy 함수로 대체하여 call 했는지 검사할 수 있는 프로세스.
그러면 mocking 자체는 spy 함수 없이 사용하는 것은 의미가 없다고 보면 될까요?
답변 2
2
안녕하세요!
강의 내용보다 더 나아가서 정리를 한 번 해봤는데 이 내용을 보고 헷갈리시면 한 번 더 남겨주세요!
spy
스텁과 유사하지만 구현된 객체가 어떠한 인자와 몇 번 호출되었는지 확인할 수 있습니다.
주로, 어떠한 요소에 등록된 이벤트 리스너가 호출되었는지 확인할 때 spy를 사용할 수 있습니다.
spy를 사용한 테스트의 가장 큰 문제로는
특정 함수가 호출되었는지만 알려줄 뿐, 올바르게 동작하는지는 말해주지 못합니다.
또한, 대상 시스템의 상세 구현방식을 활용한다는 점이 좋지 않습니다.
그럼에도 적합한 경우가 있는데,
실제 구현이나 가짜 객체를 이용할 수 없고, 상태 테스트가 불가능 한 경우 대비책으로 특정 함수가 호출된다는 확신으로 진행합니다.
함수 호출 횟수나 순서가 달라지면 기대와 다르게 동작하는 경우 상태 테스트로는 검증이 힘들기 때문에 적합할 수 있습니다.
it('클릭 이벤트 리스너가 등록되어야 한다.', () => {
const spy = jest.fn();
addClickEventListener(button, spy);
button.click();
expect(spy).toHaveBeenCalled();
});
Mock
실제 객체와 동일한 동작을 하도록 만들어진 모의 객체입니다.
네트워크 통신에 필요한 axios 라이브러리를 대체하는 jest-mock-axios와 같은 라이브러리가 Mock의 대표적인 예시입니다.
mockAxios는 axios의 네트워크 통신에 필요한 요청과 응답을 모두 대체하며, 이 과정에서의 상황을 기록해두기 때문에 요청 url이나 파라미터를 모두 검증할 수 있습니다.
실제 객체와 거의 동일하게 구현되었거나 대체하기 쉽게 추상화된 Mock 객체는 실제 객체로 대체하여 테스트를 실행할 때도 매우 유용하게 사용할 수 있습니다.
다만 실제 모듈의 명세와 동일한 Mock객체를 구현하는 것은 큰 비용이 들며 이러한 모의 객체를 남용하는 것은 테스트 코드의 신뢰성을 떨어뜨릴 수 있습니다.
import mockAxios from 'jest-mock-axios';
it('...', () => {
let catchFn = jest.fn();
let thenFn = jest.fn();
addUser('user1')
.then(thenFn)
.catch(catchFn);
expect(mockAxios.post).toHaveBeenCalledWith('/addUser/', { data: 'user1' });
expect(thenFn).toHaveBeenCalled();
expect(catchFn).not.toHaveBeenCalled();
});
0
답변해주셔서 감사합니다.
제가 부족하여 둘의 차이를 아주 명확하게 이해하기는 어려운데,
spy 함(vi.spyOn()
) 같은 경우는, 기존 생성되어 있는 함수 자체를 감싸고 함수 자체를 call 할때 몇 번 call 했는지 인자는 어떤 것이 들어왔는지를 체크하는 함수이고
mock 함수 (vi.fn()
) 같은 경우 기존 구현체를 가짜로 구현하여 가짜 결과 값(실제와 비슷)을 내뱉어 검증하는 것으로 이해했습니다. 가짜 결과 값을 만들기 때문에 사이즈가 큰 모듈 같은 경우 사용하기 유용하고, 말씀 주신 jest-mock-axios 같은 mock 라이브러리는 axios 라이브러를 대체하기 위해 만들어진 라이브러리라고 이해했습니다.
2.4 React Testing Library와 컴포넌트 테스트 섹션에서
const spy = vi.fn()
자체를 스파이 함수라고 지칭하셔서 mock 함수와 spy 함수가 헷갈리는 부분이 있었습니다.
vitest 공식 홈페이지(https://vitest.dev/guide/mocking.html#functions) 모킹 함수는 spying, mocking 두 가지로 나누고 있다는 것을 발견했는데 요 둘을 구분하는 것과 무척 헷갈리는 부분이 있습니다 ㅠㅜ
2.4 React Testing Library와 컴포넌트 테스트 섹션에서 사용하신 const spy = vi.fn()
함수 자체가 spy 역할을 하기 때문에 그렇게 사용하신 거라고 이해했습니다.
코드 조커, 오프님. 이제 어느정도 이해가 가는 것 같습니다. spyOn, fn은 내부 구현체가 같게 만들어져있군요.. 그래서 어느 목적에 따라 사용할 수 있고 단지 목적을 위해 사용할 때 이름을 붙여주기만 하면 되는군요!
사실 어떻게 쓰는지 목적에 따라 이름을 바꿔 부를 수도 있고 테스트를 작성하는데 중요한 부분은 아니지만, 제가 테스트를 처음 접하는 입장에서는 이 개념을 명확하게 짚고싶은 욕심이 있었나 봅니다..
설 연휴인데, 바로 답장을 달아주셔서 감사하다고 생각하고 있습니다 ㅠㅠ 사실 오늘 바로 답장을 달아주실지 모르고.. 정말 감사드립니다! 새해복 많이 받으세요 :) 좋은 강의 내주셔서 감사합니다!!
어유 전혀 욕심이 아닌 좋은 질문이었습니다.
추가로 들으시다가 애매한 부분이 있다면 언제든지 편하게 질문 주세요!
여유 있을 때마다 최대한 빨리 답변 남기겠습니다.
저희 강의 재밌게 봐주셔서 감사하고, 새해 복 많이 받으세요!
안녕하세요!
우선, 설 연휴이신데 저희 강의 열심히 들어주셔서 감사합니다.
충분히 헷갈리실만한 부분이 있다고 생각합니다.
(사실 이를 명확하게 구분하는게 테스트를 잘 작성하는데 매우 크고 중요한 부분을 차지하지는 않는다고 개인적으로는 생각합니다.)
주신 문서에 나와있는 것처럼 vi.spyOn()을 사용하면 인수가 호출되었는지 확인할 수 있지만 구현을 바꿀 수 없으며, vi.fn()을 사용하면 구현을 변경할 수 있습니다.
테스트의 목적에 맞게 다양하게 각각 사용할 수 있으며, 내부적으로는 vi.fn()과 vi.spyOn()모두 동일한 메서드를 공유해 구현되어 있다고 되어있습니다.
아마 강의에서는 테스트의 해당 모킹 함수가 어떤 목적의 검증으로 쓰였는지에 따라 다르게 불렀던 것 같네요. (혼란을 충분히 줄 수 있는 지점이라 생각합니다. 🙇♂)
이해하기 쉽도록 vi.fn()은 딱 떨어지는 테스트 더블 타입에 종속되기는 어려우며 모킹 함수를 사용 목적에 따라 각각 사용할 수 있다 정도로 정리하는 건 어떨까요?
추가로 궁금하신 점 남겨주세요!