개요
안녕하세요 이번 포스트에서는 msw와 cypress를 같이 사용하며 cypress의 intercept,waitfor등 api연동 작업이 안되는 문제에 관해 포스팅하겠습니다. 왜 안되는지에 대해 살펴보기에 앞서 두개의 라이브러리가 어떤식으로 동작하는지 알아보겠습니다.
msw의 동작방식
MSW(Mock Service Worker)는 API 요청을 가상으로 처리해줄 수 있게 하는 도구입니다. 브라우저에서 실행되는 서비스 워커를 이용해 클라이언트 측의 HTTP 요청을 가로채며, 이를 통해 실제 API 서버가 없더라도 API 호출을 테스트할 수 있게 도와주는 도구입니다.
네트워크 계층에서 실제로 요청을 가로채기때문에,실제 API 서버 없이도 클라이언트 코드를 자연스럽게 테스트하거나 개발할 수 있게 도와줍니다.
1
2
3
4
5
6
7
8
9
import { setupWorker, rest } from 'msw'
const worker = setupWorker(
rest.get('/api/user', (req, res, ctx) => {
return res(ctx.json({ name: 'KimChi' }))
}),
)
worker.start()
위와 같은 방식으로 MSW는 클라이언트의 API 요청을 가로채 가상 응답을 반환합니다.
cypress의 api 동작 방식
Cypress는 웹 애플리케이션을 위한 e2e나 통합테스트를 하기위 한 테스트도구로써 API 요청을 모니터링하고 제어할 수 있는 다양한 기능을 제공합니다. 그 중 cy.intercept()와 같은 명령어는 네트워크 요청을 가로채고, 특정 요청이나 응답을 조작하거나 대기 상태를 감지하는 데 사용됩니다.
Cypress 기본 API 활용
Cypress에서 cy.intercept()를 사용해 네트워크 요청을 모니터링하는 방식은 다음과 같습니다.
1
cy.intercept('GET', '/api/user', { name: 'KimChi2' })
문제점 하지만 MSW와 Cypress를 함께 사용하는 경우, Cypress의 내부 함수인 intercept, waitFor와 같은 기능들이 동작하지 않는 문제를 겪을 수 있습니다. 왜일까요?
Cypress는 네트워크 계층에서 요청을 가로채기 위해 웹소켓을 사용하는 반면 MSW는 HTTP 요청을 가로채기 때문에 두 도구가 서로 다른 레벨에서 동작하는 점에서 충돌이 발생하게 됩니다. 이로 인해 Cypress는 MSW에서 처리된 요청을 감지하지 못하고, intercept나 waitFor가 정상적으로 작동하지 않는 것이죠.
이 문제는 MSW의 GitHub 이슈에서 처음 보고된 바 있습니다. https://github.com/mswjs/msw/issues/389
해결 방법
이를 해결하기 위해, Cypress에서 커스텀하게 명령어를 설정할수 있으며 이 기능을 사용하여 msw를 사용하면서 cypress의 기능들을 사용 할 수 있습니다.예를 들어 interceptRequest라는 커스텀 명령어를 만들어서, MSW에서 처리된 요청을 Cypress의 intercept처럼 처리할 수 있도록 설정할 수 있습니다.
먼저 customResponse 함수를 추가해줍니다req.passthrough()로 요청을 그대로 처리하도록 구성되어 있습니다. 이 구조 덕분에, Cypress와 MSW가 충돌 없이 함께 동작할 수 있게 도와줍니다.
1
2
3
4
5
6
7
8
9
10
const interceptHandler = (req: any, res: any, ctx: any, fn: any) => {
function customResponse(...args: any) {
const response = res(...args);
return response;
}
if (!fn) return req.passthrough();
return fn(req, customResponse, ctx);
};
그리고 interceptHandler 함수를 추가해줍니다. 이 함수는 MSW와 Cypress 사이에서 API 요청을 어떻게 처리하고 가공할지를 정의해주는 핵심 역할을 합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Cypress.Commands.add(
'interceptRequest',
(type: UppercaseRestMethods | LowercaseRestMethods, route: string, ...args: any) => {
const method = type?.toLowerCase() as LowercaseRestMethods;
worker.use(
rest[method](route, (req: any, res: any, ctx: any) => {
try {
return interceptHandler(req, res, ctx, args);
} catch (err) {
console.error('에러', err);
}
}),
);
},
위 코드를 통해, MSW가 요청을 처리한 후 Cypress에서 해당 요청을 추적할 수 있게 됩니다. 즉, MSW가 가로챈 요청을 Cypress가 인식하고, 필요한 경우 Cypress의 intercept 메커니즘을 활용해 처리할 수 있게 되는 것입니다.