Playwright 도입기 (테스트 환경 분리, 자동 로그인)

hyerin·2024년 10월 29일
post-thumbnail

이번에 Playwright라는 e2e 테스트 툴을 처음 알고 써봤는데, 적용 과정과 테스트 효과가 너무 만족스러웠다! 혹시 Playwright 가 뭔지에 대해서 알고 싶거나, 도입을 고민하고 있는 개발자분들께 도움이 될 것 같아 우리 프로젝트 방끗에 Playwright를 도입한 경험을 자세하게 풀어보고자 한다!😁(썸네일은 playwright로고가 신기해서 직접 그린건데.. 자세히 보니 웃는 얼굴이 테스트 success를 의미하는듯??!)

1. e2e 테스트의 필요성을 느끼다!

우리 프로젝트인 방끗은 RTL로 간단한 hook테스트, 로직 테스트는 하고 있었지만, e2e테스트는 따로 진행하지 않고 있었다. 사실 프로젝트를 열심히 '개발'하는 단계에서는 기획, 디자인 등의 변경으로 소스 코드의 변경이 잦아 그와 연관된 e2e 테스트도 수정가능성이 많다고 생각해 미루고 미뤘다...ㅎㅎ

하지만 프로젝트가 개발 => 리팩토링 단계에 접어들면서 작고 큰 버그들이 발생하기 시작했고, 우리 팀은 e2e 테스트의 필요성을 점차 인지하게 시작했다.

우리가 고친 부분이 다른 부분에 예상치 못하게 사이드 이펙트를 끼치는 경우가 많았기 때문이다. e2e 테스트를 안하고 수동으로 매번 테스트하는 것은 귀찮고, 완벽하게 매번 같은 테스트를 할 수도 없었다. 즉, pr이 제출될 때마다, '정말 기존의 기능들이 문제없이 잘 동작하나?' 에 때해 의구심을 가질 수 밖에 없었고, 우리가 할 수 있는 것은 배포된 사이트가 터지지 않기만을 바라는 것 뿐이었다😂

놀랍게도 pr이 dev에 머지되면 20-30%의 확률로 버그가 터졌고, 우리의 소중한 dev 서버는 그 때마다 다운되었다. (새삼 dev서버와 prod 서버를 나누는 것의 필요성을 느끼게 되었다..) 이렇게 버그가 터져도 이미 dev 로 올라간 뒤기 때문에, 버그를 찾고 그 버그를 고치는 새로운 pr이 머지되기 전까지는 dev는 터진 상태라 그 후의 작업들의 거의 불가능했다.

이러한 상황들이 계속 발생하자, 우리 팀은 런칭을 2주 앞두고 처음으로 e2e 테스트의 도입을 논의했다. 사실 좀 늦은 감이 있었지만, 2주동안 날릴 pr의 개수를 생각하면 엄청난 효과적일 걸 확신하고 있었기 때문에..! 나는 강력하게 건의했다.

test의 스택 후보로는 cypress, playwright 2개가 있었다. 사실 cypress는 미션 때 사용해본 적이 있어서 쉽게 도입할 수 있었지만, 최근 cypress를 제치고 떠오르는 playwright도 후보로 넣었다.

playwright는 위와 같이 최근 6개월간 cypress의 다운로드 수를 넘었다. 과연 사람들이 playwright를 cypress 보다 더 사용하게 된 이유가 뭔지, 과연 그 장점들이 우리 방끗 프로젝트에 적합한지 찾아보고 정리해보았다!

2. Playwright 의 장점

(1) cypress 보다 더 다양한 브라우저, 모바일 지원

playwright 는 Chromium(Chrome, Edge), Firefox, WebKit(Safari) 를 모두 지원한다. 한 API로 모든 브라우저를 지원한다는 것이 playwright의 설명이다. 참고로 다른 후보인 cypress는 Chromium 기반 브라우저에 집중되어 있고 WebKit은 공식적으로 지원하지 않는다고 한다. 즉, 브라우저 지원 폭에서는 playwright 가 승! 모바일 환경 지원 또한 playwright 가 더 좋은데, 왜냐하면 playwright는 iOS 및 Android 등 다양한 모바일 환경을 쉽게 테스트 할 수 있기 때문이다.

우리 방끗 프로젝트는 모바일 타겟이고, 실제로 사파리 브라우저에서만 css가 일부 깨지는 이슈도 있었기 때문에, safari 와 모바일 환경 지원은 매우 중요한 쟁점이었다. 참고로 playwright의 환경은 playwright.config.js 에서 쉽게 테스트 브라우저를 추가 / 삭제 할 수 있다.

//playwright.config.js
    {
      name: 'webkit',
      use: { ...devices['Desktop Safari'], storageState: 'playwright/.auth/user.json' },
      dependencies: ['setup'],
    },
   {
      name: 'Mobile Chrome',
      use: { ...devices['Pixel 5'] },
   },

(2) 병렬 처리로 인한 빠른 속도

playwright는 속도 면에서도 유리한데,
playwright는 기본적으로 하나의 테스트 파일당 하나의 워커를 실행한다. 테스트 파일이 여러개가 있어도, 각각의 워커에서 병렬적으로 실행하여 테스트 진행 속도가 빠르다.

참고로, cypress 또한 병렬 테스트가 가능한데 cypress의 공식 문서에서는 이 병렬 테스트를 성능 상의 이슈로 권장하지 않고 있다고 한다. 실제로도 런칭이 얼마 안남은 상황에서 이 병렬 적 test 실행은 시간 절약 측면에서 많은 도움이 되었던 것 같다.

또한 playwright는 테스트마다 브라우저 환경을 생성하여 테스트를 실행한다. 실제 테스트마다 브라우저를 구동하는 cypress와 달리, 환경만 구축하는 playwright의 방식은 빠르고 효율적이라고 할 수 있다. 요약하자면, 테스트 구동 속도 측면에서도 병렬 처리가 가능한 playwright가 더 낫다고 생각했다.

(3) 쉬운 문법과 await의 사용

아무리 성능이 좋아도 문법이 어려우면 playwright 의 도입을 꺼렸을텐데, 이미 RTL과 cypress를 써본 나로서는 playwright의 문법이 정말 혜자처럼 느껴졌다. 당연히 JS기반이고, 매 이벤트 구문마다 await 를 써서 해당 이벤트가 제대로 발생될 때까지 비동기로 기다리는 문법은, 직관적이고 효율적이라고 느껴졌다!

  const checklistEditButton = page.locator('button[id="checklistEditButton"]');
  await checklistEditButton.click();
  await expect(page.getByText('체크리스트 편집')).toBeVisible();

결과적으로, 우리 방끗은 playwright를 도입하기로 결정했다. 솔직히 말하자면, 실제 도입할 때 알았던 장점보다 사용하면서 느꼈던 playwright 의 장점이 더 많아서 좋은 선택이었다고 생각한다!😄 그렇다면 이제는, 내가 프로젝트에 playwright 를 어떤 방식으로 도입했는지에 대해 자세히 설명해보고자 한다.

3. 프로젝트에 Playwright 도입과정

(1) api를 어떻게 처리할까? 에 대한 고민

Playwright 를 도입할 당시, 우리 프로젝트 방끗의 상황은 '사실상 모든 기능이 구현되었지만, 잦은 리팩토링으로 기능이 성공적으로 작동됨을 보장할 수 없는 상태' 였다. 그 당시에도 가장 중요한 기능인 form 을 관리하는 전역 객체인 store를 대대적으로 리팩토링하고 있었고, 공용 컴포넌인 tab, toast 또한 리팩토링이 진행중이었기 때문이다.
api 또한 왠만큼 다 연결해 놓은 상태였지만, api 또한 v2 로 업데이트가 되고 있는 상황이어서 백엔드의 에러 상황도 계속 발생하고 있었다. 이 상황에서 우리는 테스트를 할 때 api 를 모킹할 것인지, 아니면 실제 api를 사용할 것인지를 선택해야 했다. 각 방법의 장/단점은 다음과 같았다.

내용장점단점
api를 그대로 사용dev 서버를 켜서, 서버에 api 로 실제 요청을 보내서 테스트한다.유저 플로우와 동일하게 테스트가 가능하다.프론트의 에러 뿐만 아니라, 백엔드의 에러로도 테스트가 실패할 수 있다. 즉, 에러의 정확한 원인을 찾는데 시간이 걸린다. 또한, cd 즉 자동화된 테스트가 불가능하다. cd 로 실제 서버를 켜서 서버에 요청을 보내는 것은 불가능하기 때문이다.
api를 모킹(msw 사용)msw 를 실행한 채로 테스트를 진행한다. 가짜 응답을 받고, 프론트 코드가 잘 동작하는지 확인한다.서버의 영향을 전혀 받지 않기 때문에, 에러 원인이 프론트에만 한정된다. cd 를 통한 자동화된 테스트가 가능하다.테스트가 통과하더라도 실제 api 를 사용하지 않기 때문에, 실제 prod 서버와 동작이 다를 수 있다. 또한 모킹이 힘든 테스트의 경우, 원하는 대로 테스트를 짜는데 어렵고 시간이 많이 걸린다.

당신이라면 프로젝트가 2주 남은 시점에서, 어떤 방법이 더 효율적이라고 생각하는지 묻고 싶다! 두 가지 방법의 장점과 단점이 명확하기 때문에, 우리 팀 또한 의견을 많이 교환했는데 나는 api를 그대로 사용하는 테스트를 하는 것을 주장했다. 내가 처음에 한 생각은 다음과 같았다.

2주밖에 안남았으니, api를 그대로 사용하는 테스트를 한다면 왠만한 테스트는 커버가 되지 않을까?
테스트를 api 를 모킹해서 열심히 짜봤자,실제 api 도 아니고 내가 원하는 모든 테스트를 짜려면 모킹이 너무 힘들지 않을까?

결론적으로는, 지금부터 구현하는 테스트에 대해서는 실제 api를 이용한 테스트를 짜는 것으로 결론이 났다. 하지만, 기존에 있었던 한 msw 의 테스트는 남기기로 결정했다. 엄청 간단한 테스트이기 때문에 없앨까 고민도 했는데, cd 로 돌아가는 (테스트 통과를 못하면 merge 가 막힘) 테스트 하나정도는 있어도 좋겠다 싶어서 남기기로 했다.

(2) config를 분리하자!

msw, api 를 사용하는 테스트를 모두 작성하자는 위의 결론으로 인해, Playwright 의 config를 분리해야 했다. 왜냐하면 Playwright는 webServer 를 설정해서 test 를 어느 환경에서 실행할지 정할 수 있다.

 webServer: {
   command: 'yarn dev -- --no-open',
   url: 'http://localhost:3000/',
   reuseExistingServer: !process.env.CI,
 },

위와 같이 command 를 통해 테스트를 실행할 서버를 켜준다. 우리가 원하는 것은 api, msw 두 환경에서 모두 테스트하는 것이 었기 때문에 결국 playwright 설정 파일인 playwright.config.ts 를 분리할 수 밖에 없었다.

다음과 같이 설정 파일을 분리했다!

config에는 다음과 같이 테스트 파일의 위치를 지정해주는 testDir 이라는 속성이 있다. 이 속성에 따라 테스트 대상이 지정이 되기 때문에 나는 다음과 같이 msw / api 테스트를 폴더로 분리해 주었다.

config 는 다음과 같이 작성했다.

//playwright.mock.config.js
export default defineConfig({
  testDir: './playwright/tests/mock',
//playwright.api.config.js
export default defineConfig({
  testDir: './playwright/tests/api',

참고로, package.json 도 다음과 같이 api / msw 를 따로 테스트 할 수 있도록 설정해 주었다. --config 를 사용하면 내가 만든 config 파일의 설정을 적용하여 test 를 실행할 수 있는 점을 이용했다. ( 관련 문서 )

    "e2e:mock": "playwright test --config=playwright.mock.config.ts",
    "e2e:mock-ui": "playwright test --config=playwright.mock.config.ts --ui",
    "e2e:api": "playwright test --config=playwright.api.config.ts",
    "e2e:api-ui": "playwright test --config=playwright.api.config.ts --ui",

참고로, ui 테스트 명령어를 따로 만든 것은, playwright 의 ui 테스트가 개인적으로 훨씬 직관적이고 좋기 때문이다.

일반 Playwright 테스트 화면

Playwright UI 테스트 화면

(3) 자동화된 authenticate 설정을 하자!

사실, 우리 방끗의 경우 api 를 사용하기 위해서는 사전 로그인 작업이 필요했다. 로그인 성공시 토큰을 받아오고, 그 토큰을 사용하여 그 다음의 api 요청이 가능했기 때문이다. Playwright의 경우 Authentication 기능을 제공하기 때문에, 로그인을 테스트 마다 사전에 진행하도록 설정하 수 있다. 공식문서가 잘 되어 있어서, 공식문서를 참고하는 것을 추천한다.

1. auth 파일 생성

나는 api 를 사용하는 테스트의 경우에만 Authentication 를 할 예정이었기에, api 테스트가 모여 있는 api 폴더에 auth.setup.ts 를 만들어 이 곳에 사전 로그인 로직을 만들어 주었다.

코드는 아래와 같이 작성했다.

//auth.setup.ts
import { test as setup } from '@playwright/test';
import path from 'path';

import { ROUTE_PATH } from '@/constants/routePath';

const authFile = path.join(__dirname, '../../../playwright/.auth/user.json');

setup('authenticate', async ({ page }) => {
 const username = process.env.EMAIL || '';
 const password = process.env.PASSWORD || '';

 await page.goto(ROUTE_PATH.signIn);
 await page.locator('input[name="email"]').fill(username);
 await page.locator('input[name="password"]').fill(password);
 await page.getByRole('button', { name: '로그인 하기' }).click();
 await page.waitForURL(ROUTE_PATH.home);
 await page.context().storageState({ path: authFile });
});

위 코드 중에서 아래 코드가 중요한데, 이 코드는 로그인 성공시 받아온 토큰을 파일에 저장하는 로직이다. 테스트 환경에서는 실제 브라우저가 아니니 토큰을 파일 형태로 저장해 꺼내 쓰는 것이다.

const authFile = path.join(__dirname, '../../../playwright/.auth/user.json');

2. user.json(쿠키 저장 파일) 생성

토큰을 저장하는 user.json 파일은 아래와 같이 playwright 의 하위 폴더에 auth 폴더를 만들어 넣어주었다. 당연히도, 이 파일은 중요한 토큰이 저장된 파일이기 때문에 git 에는 절대 올라가서는 안되니, env 에 꼭 추가해주자!✅

3. config 파일에 user.json 파일 연결

이렇게 auth 파일까지 만들고 난 다음에는 , config 에서 해당 user.json 파일을 쓰도록 설정해야 한다. 이는, 테스트가 돌아갈 때 쿠키 정보가 담긴 해당 파일을 사용하세요 라는 뜻이다. projects 속성에 다음과 같이 설정해주면 된다.

  projects: [
    { name: 'setup', testMatch: /.*\.setup\.ts/ },
    {
      name: 'chromium',
      use: { ...devices['Desktop Chrome'], storageState: 'playwright/.auth/user.json' },
      dependencies: ['setup'],
    },

위 설정까지 마치면 테스트를 돌릴 때 다음과 같이 auth.setup.ts 에 넣은 자동 로그인 코드(authentication)가 먼저 실행되고 받은 쿠키를 사용해 테스트가 실행된다.

4. Playwright를 통해 생긴 변화

Playwright는 런칭 데이 때 작업량이 많았을 때, 머지하기 직전에 버그들을 많이 발견해주었다. 이로 인해 작동되지 않은 코드가 dev에 올라가는 것이 방지되었고, 그로 인해 코드의 안정성과 완결성이 엄청나게 올라갔다. 수치로 표현하자면 사전에 버그를 찾을 확률은 80% 이상 올라간 것 같다. 개인적으로는 그 어떤 코드들보다 값진 코드였다고 생각한다.

5. 결론

마지막으로, 본인의 일이 바빴을 텐데도 Playwright를 초기에 세팅해주고 열띤 토론을 해준 제이드에게 고맙다는 말을 하고 싶다!

만약 당신도 프로젝트에 e2e의 도입을 생각하고 있다면, playwright를 꼭 고려하라고 권해주고 싶다. 정말 좋은 테스트 툴인 것 같다! 테스트 도입시기가 고민될수도 있지만, 우리처럼 어느정도 개발이 된 이후에 진행해도 늦지 않고, 그 때 도입하는 것이야 말로 e2e의 제대로된 장점을 느낄 수 있을지도 모른다는 말을 전하며 포스팅을 마친다.

https://shorttrack.tistory.com/7
https://velog.io/@sosoyim/e2e-%ED%85%8C%EC%8A%A4%ED%8A%B8-%EB%8F%84%EA%B5%AC%EC%9D%B8-Playwright-%EC%84%A0%ED%83%9D-%EC%9D%B4%EC%9C%A0

profile
글쓰기의 시작은 나를 위해, 끝은 읽는 당신을 위해

0개의 댓글