MSW 사용하여 테스트 코드 작성하기 with GraphQL, react-query

valley
더핑크퐁컴퍼니 기술 블로그
9 min readJun 1, 2022

서론

안녕하세요. 더핑크퐁컴퍼니 웹개발팀에서 프론트엔드 개발 업무를 맡고 있는 밸리입니다.🐰

기존 프로젝트는 GraphQL과 Apollo Client의 조합으로 개발하였고, 테스트를 진행할 때는 Apollo Client에서 제공하는 MockedProvider를 사용하여 테스트를 진행했었습니다.

새로운 프로젝트에서 GraphQL과 react-query의 조합으로 개발을 진행하게 되면서, 어떻게 테스트를 진행할지에 대해 고민을 하게 되었습니다.

react-query 공식문서에는 @testing-library/react-hooks 를 이용한 테스트 방법이 기재되어 있으나,

단순 react-query hooks에 대한 테스트뿐만 아니라, 다양한 케이스의 API request, response로 렌더링된 요소들에 대한 테스트를 진행하기 위해서 다른 방법을 고민해보아야 했습니다.

애초에 테스트 환경에서는 실제 서버에 API 요청을 날리는 경우는 많지 않습니다. 프론트엔드 테스트인데, 서버의 API 가 실제로 작동하고 안 하고는 서버쪽의 관심사이기 때문입니다.

또한 실제 서버에 API Call을 날리면 여러 리스크가 생길 수 있습니다.(물론 서버에 직접 API Call을 하는 테스트도 있습니다.)

따라서 테스트 환경에서 API request, response를 Mocking하는 작업이 필요했습니다.

@testing-library/react-hooks 을 활용한 테스트 예제는 아래 공식 문서를 참고 하세요!

https://react-query.tanstack.com/guides/testing#_top

Mock Service Worker(MSW)

고민 끝에, 저희팀에서는 Mock Service Worker (MSW) 라이브러리를 채택했습니다.

MSW는 API Mocking 라이브러리로, 서버로 가는 네트워크 요청을 가로채서 mocked response를 내려주는 역할을 합니다.

즉 API를 Mocking 해야 하는 모든 상황에서 유용하게 쓰일 수 있습니다.

예로 프론트엔드 개발자가 백엔드 개발자로부터 아직 개발에 필요한 API 를 받지 못한 상황이라면, MockData를 작성해두고, 화면을 구상할 수 있을 것 입니다.

하지만 이는 네트워크 레벨에서 사용해 볼 수 없다는 단점을 가지고 있습니다.

네트워크 레벨에서 통신하는 게 아니므로 axios 나 fetch 도 사용할 수가 없을뿐더러, 네트워크 통신 속에 발생하는 여러 상황들에 맞추어 처리해 놓은 UI 부분에 대해서는 (loading이나 error 등) 완벽한 테스트를 해볼 수가 없습니다.

이럴 때 MSW를 사용하면, Mock 서버 없이도 API 요청들을 네트워크 레벨에서 Mocking 하여 사용할 수 있게 됩니다.

MSW는 어떻게 Mocking한 API 요청들을 네트워크 레벨에서 사용할 수 있게 하는 것 일 까요?

Service Worker를 사용하는 MSW

우선 MSW의 내부 동작 원리를 보면 아래와 같이 동작하는 것을 알 수 있습니다.

중간에 보면 Service Worker 라는 것이 보이시나요?

MSW는 Service Worker를 통해 네트워크 요청을 가로채고 모의 응답을 반환합니다.

Service Worker란 무엇일까요?

서비스 워커는 웹 응용 프로그램, 브라우저, 그리고 (사용 가능한 경우) 네트워크 사이의 프록시 서버 역할을 합니다.

MDN에 기재되어 있는 Service Worker의 설명입니다.

MSW는 Service Worker를 통해 네트워크 사이의 프록시 서버 역할을 하고 있는 것입니다.

Service Workder API는 모던 브라우저에서 제공하는 표준 API이므로 타 라이브러리들과의 종속성 문제나 호환성 문제를 고려하지 않아도 된다는 장점이 있습니다.

핵심은 Service Worker 는 앱을 구동하는 메인 스레드와는 다른 스레드(백그라운드 스레드)에서 동작합니다. 즉, 논블로킹으로 작동하므로 메인 스레드의 연산을 가로막지 않습니다.

❗️ 단, 주의할 점이 있습니다.

  • 서비스 워커는 보안 상의 이유로 HTTPS에서만 동작합니다. 네트워크 요청을 수정할 수 있다는 점에서 중간자 공격에 굉장히 취약하기 때문입니다.
  • Firefox에서는 시크릿 모드에서 Service Worker API에 접근할 수 없습니다.
  • IE 에서는 사용이 불가능합니다.

MSW Install

npm install msw --save-dev
# or
yarn add msw --dev

Mock definition

MSW를 사용할 때 Mocking 해둘 작업들을 미리 정의할 수 있습니다.

저는 테스트 환경에서만 MSW를 사용할 예정이므로 아래와 같은 폴더 구조를 만들었습니다.

handlers 폴더 안에 유저 정보를 불러오는 getUserInfo.ts 파일을 만들고, Query를 mocking하여 넣어보겠습니다.

handlers/getUserInfo.ts

그리고 이러한 핸들러들은 여러 개가 생길 수 있으므로 index.js 를 만들어 여러 핸들러를 한번에 export 해주는 파일을 만들어 주고, 방금 만든 handler를 import해서 넣어 줍니다.

handlers/index.js

테스트 환경은 브라우저가 아닌 Node 환경에서 진행합니다. 하지만 Service Worker API는 브라우저 환경에서만 사용할 수 있습니다. 즉 Node 환경에서는 사용할 수 없습니다.

이를 위해 MSW 내부에서는 https or XMLHttpRequest 확장하는 방식으로 처리가 되어있습니다. 따라서 Node 환경에서도 MSW를 사용할 수 있습니다.

Create Server Instance

앞서 만들어둔 handler를 사용해 server instance를 만들겠습니다.

/mocks/server.js

이번 프로젝트는 테스트 시에 Jest 환경에서 테스트를 진행하므로, 셋업 파일에 아래와 같이 세팅을 진행하였습니다. 여러분들의 테스트 셋업 파일이 있다면, (ex)setupTests.js) 해당 파일에 아래 코드를 작성하면 됩니다.

jest.setup.js

jest.config.js

렌더링 테스트

Mocking한 Query API를 통해 렌더링한 요소들에 대한 UI 테스트를 진행해 보겠습니다.

react testing 도구는 @testing-library/react 라이브러리를 사용했습니다.

해당 라이브러리에 대한 이야기는 아래 링크에서 보실 수 있습니다.

👉🏻 react-testing-library-의-fireevent-를-userevent로-마이그레이션-하기

우선 렌더링 테스트하기 위한 기본적인 세팅들이 필요합니다. 이러한 세팅들을 여러 테스트 파일에서 모두 작성해도 되지만, 그렇게 하면 많은 중복 코드가 생성되기 때문에 공통 세팅을 묶어 제공하는 Randerer 컴포넌트를 만들어 두겠습니다.

test-utils/customTestRanderer.tsx

유저 인터렉션 테스트를 위해 @testing-library/user-event 를 사용했습니다.

user-event 기본 세팅 하는 부분도 여러 곳에서 동일하게 사용될 예정이므로 기본 세팅을 마친 userEvent를 모듈화하도록 하겠습니다.

customUserEvent.ts

이제 테스트 파일을 하나 만들어서, 유저의 이름을 화면에 그려주는 UserName 컴포넌트를 테스트해보겠습니다.

❗️ 주의할 점은 MSW에서 GraphQL API의 네트워크 요청을 가로채려면, 반드시 Operation name이 같아야 합니다. 예로 user 정보를 불러오는 쿼리의 Operation name은 ‘GetUserInfo’ 를 사용했습니다. handlers/getUserInfo.ts 에도 Operation name 자리에 ‘GetUserInfo’ 를 적어 두었습니다.

UserName.tsx

이제 테스트를 진행할 수 있습니다. 🎉 지금까지 만들어둔 모듈들을 이용해 아래처럼 테스트 코드를 작성하시면 됩니다.

tests/UserName.test.tsx

끝으로

이번 글에서는 MSW를 활용해 API를 Mocking하고, 테스트 코드를 작성하는 과정을 담아 보았습니다.

이번 프로젝트에서는 이미 서버 API가 거의 개발이 완료된 상태에서 프론트 개발 작업이 들어가게 되어 브라우저 환경에서는 별도로 사용하진 않았지만, 추후 서버 개발이 완료되기 전에 프론트엔드 작업을 해야 하는 상황에서 MSW를 활용하면 굉장히 높은 수준의 완성도를 가진 작업물을 개발할 수 있을 것 같습니다.

읽어주셔서 감사합니다.🙂

참고 문서

--

--