인프런 커뮤니티 질문&답변

황선태님의 프로필 이미지
황선태

작성한 질문수

실무에 바로 적용하는 프런트엔드 테스트 - 1부. 테스트 기초: 단위・통합 테스트

3.2. 모듈 모킹(Mocking)

mocking과 spy함수가 헷갈립니다.

해결된 질문

작성

·

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 역할을 하기 때문에 그렇게 사용하신 거라고 이해했습니다.

코드 조커, 오프님의 프로필 이미지
코드 조커, 오프
지식공유자

안녕하세요!
우선, 설 연휴이신데 저희 강의 열심히 들어주셔서 감사합니다.

충분히 헷갈리실만한 부분이 있다고 생각합니다.
(사실 이를 명확하게 구분하는게 테스트를 잘 작성하는데 매우 크고 중요한 부분을 차지하지는 않는다고 개인적으로는 생각합니다.)


주신 문서에 나와있는 것처럼 vi.spyOn()을 사용하면 인수가 호출되었는지 확인할 수 있지만 구현을 바꿀 수 없으며, vi.fn()을 사용하면 구현을 변경할 수 있습니다.

테스트의 목적에 맞게 다양하게 각각 사용할 수 있으며, 내부적으로는 vi.fn()vi.spyOn()모두 동일한 메서드를 공유해 구현되어 있다고 되어있습니다.

아마 강의에서는 테스트의 해당 모킹 함수가 어떤 목적의 검증으로 쓰였는지에 따라 다르게 불렀던 것 같네요. (혼란을 충분히 줄 수 있는 지점이라 생각합니다. 🙇‍♂)

이해하기 쉽도록 vi.fn()은 딱 떨어지는 테스트 더블 타입에 종속되기는 어려우며 모킹 함수를 사용 목적에 따라 각각 사용할 수 있다 정도로 정리하는 건 어떨까요?

추가로 궁금하신 점 남겨주세요!

황선태님의 프로필 이미지
황선태
질문자

코드 조커, 오프님. 이제 어느정도 이해가 가는 것 같습니다. spyOn, fn은 내부 구현체가 같게 만들어져있군요.. 그래서 어느 목적에 따라 사용할 수 있고 단지 목적을 위해 사용할 때 이름을 붙여주기만 하면 되는군요!

사실 어떻게 쓰는지 목적에 따라 이름을 바꿔 부를 수도 있고 테스트를 작성하는데 중요한 부분은 아니지만, 제가 테스트를 처음 접하는 입장에서는 이 개념을 명확하게 짚고싶은 욕심이 있었나 봅니다..

설 연휴인데, 바로 답장을 달아주셔서 감사하다고 생각하고 있습니다 ㅠㅠ 사실 오늘 바로 답장을 달아주실지 모르고.. 정말 감사드립니다! 새해복 많이 받으세요 :) 좋은 강의 내주셔서 감사합니다!!

코드 조커, 오프님의 프로필 이미지
코드 조커, 오프
지식공유자

어유 전혀 욕심이 아닌 좋은 질문이었습니다.
추가로 들으시다가 애매한 부분이 있다면 언제든지 편하게 질문 주세요!
여유 있을 때마다 최대한 빨리 답변 남기겠습니다.

저희 강의 재밌게 봐주셔서 감사하고, 새해 복 많이 받으세요!

황선태님의 프로필 이미지
황선태

작성한 질문수

질문하기