현재 진행하는 출시를 앞둔 e-commerce 프로젝트에서 E2E 테스트를 맡아서 진행하게 되었다.
이 프로젝트를 몇 달 째 진행중이지만, 시작하는 초반에도 유닛 테스트 또는 E2E 테스트에 대한 논의가 이루어졌지만 도입하지 않기로 했었다. 왜냐하면 프로젝트 시작할 때, 스타트업이 다들 그렇듯이, 기획이 확실하게 정해지지 않아서 전체 및 세부적인 기능 및 디자인 등이 계속 요동쳤기 때문이다. 게다가 비즈니스 팀에서는 빠른 출시를 원했다. 그리고 실사용자를 위해 베타 테스트를 프로덕트 출시 이전에도 주기적으로 배포하고 있었다. 그러한 상태에서 유지보수가 필요한 테스트까지 도입되면 기획이 변경될 때마다 테스트도 이에 맞춰줘야 했고, 이러다보면 개발 속도는 최소 1.5배 정도는 더 걸렸을 것이다. 이러한 상태가 지속되면,
"이번 배포 때만 테스트 하지말고 배포하고, 다음부터 테스트 본격적으로 시작해보죠."
라는 말이 나올 것이고, 그 본격적인 시작이라는 다음은 그 다음, 그 다음이 되고, 결국엔 테스트 필요없는데 잘 굴러가네? 할 필요 없는 것 같은데? 라는 말까지 나올 것이 뻔히 예상됐고, xxx.test.ts 등의 파일은 없는 곳도 있고, 있는 곳도 있는 정말 전형적인 레거시가 될 것이 눈 앞에 선헸다. 예전에 테스트를 도입했을 때도 그랬던 예화가 있었기 때문에.
다행히도 개발을 어떻게 진행할 것인지, 즉 테스트 도입 여부라든지, 이번 스크럼 때 개발할 피처라든지 등은 개발팀에게 자율권이 어느정도 보장됐기 때문에, 테스트 도입은 유보됐었다. (동시에 불행이기도 했던 건, 이런 자율권이 보장됐던 이유가 사수없는 주니어, 중니어로만 구성됐던 개발팀이기도 했기 때문이다.) 이제 출시를 앞둔 지금, 기획이 안정을 찾는 지금, E2E 테스트를 본격적으로 시작하게 되었다는 시작글이다.
솔직히 말하면 그 때 당시엔 나도 테스트에 대해 자신이 없었다. 테스트를 깨작깨작 해본적은 있지만 현업에 사용될 수준으로 진행해본적은 없었고, Unit - Integration - E2E test 중에서 모두 다 진행할 리소스는 없었고, 어떤 테스트를 진행해야 할지 감이 잡히지 않았다. 기억해보면 개인적으로 프론트 엔드에서 단위 테스트(unit test)는 회의적인 시각이 있었다.
프론트엔드에서 보여주는 가격, 숫자 등의 값은 사실 서버에서 내려준 것이 거의 전부이고, 이걸 어떻게 UI/UX 적으로 잘 꾸며서 보여주느냐가 SPA에서 기본적인 프론트엔드의 역할이라고 생각했기 때문에, 단위 테스트의 대상이 되는 함수, 로직, 훅 등을 생각해 볼 때, 단위 테스트는 붙이려면 백엔드에 붙여야한다고 생각했다.
프론트에서 리액트 UI 컴포넌트에도 단위 테스트를 붙일 수 있다. 대부분 props 로 내려준 값들이 원하는 값으로 변형되어 잘 나오느냐에 대한 것이 테스트 대상이 될 것인데, 괴랄하게 개발하지 않는 이상 props로 내려주면 당연히 잘 나오는 값들에 대해, 디자인, UX 등의 변경이 발생할 때 이를 반영해줘야 한다는 작지만 한편으로 큰 유지보수 비용으로 테스트를 해야하는지 의문이었고, 결국 프론트에서 단위 테스트는 불필요하다는 생각이 더 컸다.
물론 다 테스트하면 좋다. 단위 테스트/통합 테스트/E2E 테스트까지 다하면 좋다. 그런데 문제는 항상 리소스이다. 시간과 인력이 부족한 상황에서는 최선의 선택이 필요하다.
결과적으로 우리 팀의 리소스와 여러 상황들을 고려했을 때, 단위 테스트는 주문, 계산 등의 값을 내려주는 백엔드에서 수행하는 것으로 정하고 전체적인 흐름은 e2e 테스트로 실행하기로 어느정도 가닥이 잡혔고, e2e 테스트만 우선 도입하기로 결정이 되었다. 그리고 여력이 있다면 폼을 이용한 주문 생성 등은 프론트에서 통합 테스트로 수행하고.
그때까진 몰랐다. E2E 테스트가 이렇게 어렵고 예민할 줄은..
E2E 테스트 툴은 Playwright 로 정했다. 오픈소스이고, MS 에서 개발했기 때문에 Azure DevOps 와 잘 맞을 것이라고 생각했기 때문이다.
1차적인 목표는 배포 할 때 smoke test 정도로 테스트를 진행하는 것이었다. 주기적으로 배포할 때마다 기능이 잘 작동하는지 손으로 매뉴얼 테스트를 했기 때문에, 우선 배포 전 또는 후로 CI 파이프라인에 붙이는 것이 목표였다. 그 이후에 모든 pr 에 붙이는 것이 2차적인 목표였다.
파이프라인에 붙이면서 yaml 파일을 구성해서 진행했다. Playwright 공식 문서에도 CI 에 대한 부분이 잘 나와 있어서 어느 정도 잘 작성할 수 있었다. Pipeline을 돌리기 전에 DevOps 에서 테스트에 대한 환경 변수를 넣어주고, 이는 yaml 파일에서 주입되어 사용되고, yaml 파일은 다시 테스트 파일에 넣어주고 하는 부분들이 굉장히 재미있었다. 상품, 가격, 옵션 등에 대한 환경변수에 무엇을 주입할지 잘만 구성해주면 테스트가 정말 재밌어질 것 같았다. 물론 뻘짓/삽질을 많이 했다. 환경변수를 이상한 형식으로 넣어놓고 왜 안되냐고 몇시간동안 디버깅하는 등의..
매번 실행되는 npm ci 과 playwright browser 설치에 대한 시간을 줄이기 위해 cache 를 도입했고(사실 현업에서 지금까지 없었다는 게 어쩌면 기가찰 노릇이지만), 테스트 실패 또는 성공시마다 Artifact 와 JUnit .xml 파일을 생성해서 파이프 라인 결과 화면에서 한눈에 어디가 틀렸는지 파악하고, 실패시에는 생성된 trace 파일을 이용해서 디버깅을 할 수 있도록 만들었다.
주의해야 할 점은 로컬 터미널에서 테스트를 돌릴 때와 CI 환경에서 테스트를 돌릴 때 차이점이었다.
CI 에서 환경은 굉장히 예민하고 느리다. 게다가 UI를 표시하지 않는 Headless 모드로 실행되기 때문에 UI 가 변경되거나 애니메이션 등이 원인이 되어 로컬에서는 성공하지만 CI 환경에서는 번번히 실패하게되는 일들이 발생한다. 이를 flaky 테스트라고 하는데, 같은 코드를 가지고 같은 환경에서 실행해도 실행할 때마다 결과가 다르게 나오는 테스트를 말한다. 즉 테스트 자체가 불안정하거나 신뢰할 수 없는 상태인 것이다.
이를 해결하기 위해서 CI 환경에서는 테스트가 잘 따라오고 있는지 아래 코드처럼 항시 확인을 해줘야 한다.
// 버튼 클릭하기 전에 버튼이 있다고 전제하지 말고, 먼저 버튼이 있는지 먼저 확인
await expect(page.locator('button')).toBeVisible();
await page.locator('button').click();
// CI 환경에선 파일 업로드의 input.click() 이 작동하지 않는다.
const fileInput = page.locaor('input#file-input');
await fileInput.click() // --> CI 환경에서 에러 발생
await fileInput.setInputFiles('test/sample.stl');
어찌저찌 기본적인 테스트는 성공했고, 아직 작성해야할 테스트 시나리오가 엄청 많지만, 이번 e2e 도입은 나에게 있어 작업 영역이 단순 코드 작성을 넘어서 파이프라인까지 경계를 확장했다는 것에 의미가 있는 것 같다. 매번 새로운 것을 도입하고 배울 때마다 너무 재밌고 짜릿한 것을 보면 개발이 너무나 잘맞는 다고 느껴진다.
### Further
이번 테스트 및 개발을 하다보면서 고쳐야할 점들이 보였다.
npm은 의존성 관련해서 별로 사용할만하지 않아서 pnpm 등으로 넘어가야겠다는 것을 파이프라인 cache 를 진행하면서 다시 한번 공감했고, 모노레포로 결국엔 넘어가겠구나라고 공통 기능들을 사용하고 관리하면서 알게되었다.
Are Unit Tests Really Just Garbage? - https://dev.to/nuxt-wimadev/are-unit-tests-really-just-garbage-3c3
Are Unit Tests Really Just Garbage? 💩😳
I always assumed that unit tests are just part of the job. They are just there, and it's a no-brainer...
dev.to
Your opinion on frontend testing - https://www.reddit.com/r/webdev/comments/1bgu2gh/your_opinion_on_frontend_testing/
From the webdev community on Reddit
Explore this post and more from the webdev community
www.reddit.com
Unit tests pointless - https://www.reddit.com/r/Angular2/comments/1h580qc/unit_tests_pointless/?rdt=32818
From the Angular2 community on Reddit
Explore this post and more from the Angular2 community
www.reddit.com
Playwright CI - https://playwright.dev/docs/ci
Continuous Integration | Playwright
Introduction
playwright.dev
'웹 개발' 카테고리의 다른 글
pnpm 1편 - (부제: "왜 npm ci --legacy-peer-deps를 쓰나요?") (3) | 2025.06.07 |
---|---|
Polyfill (1) | 2024.10.01 |
번들러와 기능 (0) | 2024.09.20 |
로컬 스토리지 (Local Storage) | Web Storage API (0) | 2024.07.06 |
캐싱 전략 - Cache-Control (0) | 2024.05.29 |