본문으로 건너뛰기

E2E 테스트 도입 경험기

· 약 10분
방경민
방경민
Frontend Developer
🔗이 글은 카카오엔터테인먼트 기술 블로그 (2023-02-09)에 게시된 글을 옮겨온 것입니다.원글 보기 →

최근 저희 FE 그룹 내에서 프로젝트들에 E2E 테스트를 도입해 보자는 이야기가 나오기 시작했고 실제로 적용하는 사례들이 많아지기 시작했습니다. 제가 진행하던 프로젝트에도 E2E 테스트를 도입하기 위한 사전 조사를 하게 되었는데, 유닛 테스트나 통합 테스트의 활발한 도입 사례들에 비해 E2E 테스트의 성공 사례는 손에 꼽을 정도였습니다. 특히, 단순 도입 외에 성공적인 운영사례는 정말 찾기 어려웠죠. 그래서 우리가 성공적으로 E2E 테스트를 도입하고 운영하기 위해 어떤 것들을 생각해 보고, 어떤 것들을 실천했는지 공유하고자 합니다.

E2E 테스트란?

E2E 테스트는 End To End 테스트의 약자로 애플리케이션의 흐름을 처음부터 끝까지 테스트하는 것을 의미합니다. 유닛 테스트나 통합 테스트는 모듈의 무결성을 증명할 수 있는 강력한 테스트이지만, 모듈의 무결성이 애플리케이션 동작의 무결성까지는 증명해 줄 수 없습니다. 그래서 E2E 테스트 과정에서는 실제 사용자의 시나리오를 테스트함으로써 애플리케이션 동작을 테스트하게 되고, 이 테스트를 통과함으로써 애플리케이션의 무결성을 증명할 수 있게 됩니다.

다만 사용자의 시나리오를 검증한다는 것은 그만큼 테스트 과정이 길고 다양하다는 것을 의미합니다. E2E 테스트는 비용이 많이 드는 만큼 꼭 필요한 테스트만 수행해야 한다는 사실에는 대부분 이견이 없을 듯합니다.

E2E 테스트 운영의 걸림돌

그럼, E2E 테스트를 도입하기 위해서는 엄청나게 어려운 설정을 해야 할까요? 대표적인 E2E 라이브러리 중 하나이자, 많은 사랑을 받는 Cypress 설치 방법을 잠깐 보겠습니다.

npm install cypress --save-dev

npx cypress open

놀랍게도 위 커맨드가 Cypress 설치의 전부입니다. 공식 문서에 설치와 관련된 다양한 설명이 안내되어 있지만, 위 커맨드만 실행하면 Cypress 기본 설정 파일들이 생성되고 바로 Cypress runner GUI가 실행됩니다. 다른 E2E 테스트용 라이브러리인 Playwright도 마찬가지로 아주 쉬운 설치 방법을 제공합니다.

E2E 테스트는 애플리케이션의 무결성을 검증할 수 있고, 설치하기도 아주 쉽다는 걸 알았습니다. 그러면 무조건 도입하는 게 이득이 아닐까요? E2E 테스트의 강력한 장점을 생각해 보면 도입하지 않을 이유는 전혀 없어 보입니다. 하지만, 많은 분이 E2E 테스트는 어렵다고 느끼고, 어떤 회고록에서는 E2E 테스트를 걷어내기까지 했다는 것을 보면 분명히 뭔가 걸림돌이 있어 보입니다. 조금 더 성공적으로 E2E 테스트를 도입하고 운영하기 위해서는 이런 걸림돌과 해결책에 대해서 집중할 필요가 있다고 생각해서 걸림돌이 될 만한 요소들을 취합해 봤습니다.

1. QA 팀의 하위 호환

개발자의 의도를 테스트하던 유닛 테스트와 통합 테스트와는 다르게 E2E 테스트는 사용자의 동작을 테스트하는 장입니다. 이 부분은 QA 팀의 업무와 완전히 겹치는 부분이기도 합니다. 혹시나 QA 팀과 협의가 되어 시나리오를 QA 팀에서 작성하게 된다면 E2E 테스트는 QA 팀의 업무를 보조해 주는 좋은 수단이 될 수도 있겠지만, 따로 협의가 이뤄지지 않은 상황이라고 가정하면 E2E 테스트는 애매한 위치가 되어버릴 수도 있습니다.

개발자들이 하나의 피처에 대해 A ~ D까지 시나리오를 작성했다고 가정하면 QA 팀에서는 A ~ Z 혹은 그 이상의 시나리오를 테스트하고 있을 것입니다. 아무리 개발자들이 시나리오를 열심히 짠다고 하더라도 QA 팀은 이에 대한 전문성을 훨씬 더 많이 가진 집단이기 때문입니다. 그럼, 개발자들이 테스트하지 못한 E ~ Z에서는 계속 버그가 발생할 수 있을 것이고, A~D 시나리오도 QA 팀에서 중복해서 테스트할 것이기 때문에 E2E 테스트의 효용에 대해 의문을 가질 수밖에 없겠죠. 결과적으로 보면 E2E 테스트는 QA 팀에서 진행하는 QA 업무의 하위 호환이 되는 셈입니다. 장기적으로 E2E 테스트의 운영이 필요한가에 대한 의문을 낳게 될 것 같습니다.

2. 테스트 코드 관리의 어려움

경험상 테스트 코드를 관리하는 것은 생각보다 어려운 일입니다. 개발이 종료되어 유지 보수 상태에 들어간 프로젝트는 필요한 때에만 최소의 인원이 투입되어 관리하게 되다 보니 일반적으로 코드 작성 및 수정에 집중하게 되는 경향이 있습니다. 가뜩이나 유닛 테스트나 통합 테스트 같은 가벼운 테스트들도 관리하기 어려운데, 사용자의 동작을 테스트하는 E2E 테스트를 수정하는 일은 상당히 어려운 일일 것입니다.

그나마 E2E 테스트를 husky나 github action 등에 연결해두어 E2E 테스트를 통과한 경우에만 코드 병합을 허용한다면, 개발자는 어쩔 수 없이 오류가 발생한 테스트를 수정하게 될 것입니다. 하지만, 이렇게 억지로 관리되는 테스트들은 의미가 금방 퇴색되어 버리고 E2E 테스트는 시간만 잡아먹는 과정이 되어버릴 것입니다. 제대로 된 E2E 테스트를 원한다면 꾸준한 관리를 할 수 있는 방법을 준비해둘 필요가 있습니다.

3. 테스트 속도

E2E 테스트는 유닛 테스트, 통합 테스트와는 차원이 다른 시간이 소요되게 됩니다. API 응답을 기다리는 것은 물론이고 GUI를 통해 화면에 컴포넌트가 실제로 렌더링 되는 모든 과정까지 테스트하기 때문에 더욱더 많은 시간을 소요하게 됩니다. 게다가 프론트엔드 E2E 테스트에 널리 쓰이는 Cypress의 경우 유료 플랜 없이는 병렬 테스트를 제공하지 않기 때문에 테스트 시간이 더욱 길어집니다. 하나의 테스트에 6초가 걸린다고 가정하고 100개의 시나리오를 작성했다면 테스트에만 10분이 소요됩니다. 그렇다고 유료 플랜을 선택하자니 Cypress 유료 플랜도 결코 만만한 가격은 아닙니다. 여러분이 회사에서 제대로 유료 플랜을 사용하기 위해서는 최소 Business 플랜 이상은 사용해야 할 텐데, 해당 플랜의 월 사용요금은 300달러이고 지원하는 테스트도 월 10,000개로 제한되어 있습니다. 물론, 단순히 병렬 테스트를 위한 요금 지급은 아니며 대시보드 지원부터 github 연동까지 다양한 기능을 지원해준다는 점도 참고해주세요.

E2E 테스트 도입 원칙

도입 전 생각 했던 걸림돌 중 가장 큰 문제는 E2E 테스트가 QA 팀 업무의 완전한 하위 호환이라는 것이었습니다. 그렇다고 바로 QA 팀에 협업을 요청하기에는 저의 E2E 테스트에 대한 역량이나 경험이 부족했기 때문에 상당한 부담이 되었습니다. 아무래도 QA 팀의 QA 프로세스에 관여하는 부분이기 때문이었지요. 그래서 QA 팀과의 협업은 다음 프로젝트로 미뤄두기로 했습니다.

그럼에도 우리는 이번 프로젝트에 E2E 테스트를 도입해 보기로 했습니다. 아직 개발 중인 프로젝트였기 때문에 E2E 테스트를 도입하기 좋은 시기였으며, 실제로 E2E 테스트 역량을 쌓고 시행착오를 겪어 봐야 추후 다른 프로젝트에서 QA 팀과 손을 맞추어 테스트를 돌리는 아름다운 그림을 만들어볼 수 있을 것 같았기 때문입니다. 다만, 이왕 도입한다면 꾸준히 운영할 수 있는 의미 있는 E2E 테스트로서 남기를 바라기 때문에 스스로 몇 가지 원칙을 정하며 도입을 시작했습니다.

1. 기획서 기반의 시나리오 작성

E2E 테스트 시나리오 작성은 기획서에 정의된 동작을 옮겨 적는 것으로 결정했습니다. QA 팀은 기획서를 기준으로 QA를 진행하기 때문에, 기획서의 동작을 모두 시나리오로 작성한다면 QA 과정에서의 버그 발생을 최대한 줄일 수 있다고 생각했기 때문입니다. 이 과정으로 위에서 제기한 QA 팀의 하위 호환 문제를 해결할 수는 없지만, 최대한 QA 팀의 시나리오와 유사한 시나리오를 작성할 수 있게 되고 QA 팀과 협업하는 느낌을 간접적으로 느낄 수 있게 되었습니다.

이렇게 항상 기획서와 E2E 테스트 시나리오를 동기화하도록 결정하게 된다면 시나리오의 관리에도 큰 도움이 됩니다. 추후 유지 보수 상태에 돌입하더라도 새로운 피처나 기능 수정 때에는 기획서가 수정될 것이기 때문이죠. 기획서가 수정되는 시점에 시나리오도 동일하게 수정하는 것으로 시나리오를 최신화할 수 있을 것으로 생각합니다. 기획서의 업데이트는 곧 테스트 코드의 업데이트를 의미하게 되었으므로 테스트 코드 관리의 어려움 문제를 해결할 수 있습니다.

2. Mock 데이터의 사용 지양

E2E 테스트가 제대로 된 의미를 갖기 위해서는 모든 과정이 실제 환경과 동일해야 한다고 생각합니다. Mock 데이터를 사용하면 개발자가 원하는 시나리오를 쉽게 테스트할 수 있지만, 이는 다시 말하면 Mock 데이터로 만들어진 시나리오는 항상 개발자가 원하는 대로 실행되는 시나리오라는 말이 됩니다. 분명 A 시나리오에 대한 E2E 테스트는 통과했는데 실제 환경에서는 A 시나리오를 테스트하는 동안 버그가 발생할 수 있고, 이는 E2E 테스트에 대한 신뢰성을 떨어뜨리게 될 것입니다.

예시로 백엔드 API에서 오류가 발생할 수 있고, API 응답 값이 잘못 내려오고 있을 수도 있는데 응답을 Intercept 해서 시나리오에 맞게 변경해 사용한다면 이런 오류를 잡아낼 수 없을 것입니다. 그래서 E2E 테스트에는 실제 API만을 사용하는 것이 바람직하다고 생각했습니다. 만약 모종의 이유로 실제 API를 사용해 테스트할 수 없다면, QA 팀에 테스트를 위임하거나 해당 모듈만을 통합 테스트로 검증하는 방법이 있습니다.

3. Pre-Push 단계에서 E2E 검증 (혹은 GitHub Action)

테스트에 실패한 코드들이 병합되는 것을 막기 위해 husky pre-push 단계에서 E2E 테스트를 통과한 코드만 push 되도록 작업했습니다. GitHub Action을 통해 PR 단계에서 검증하는 방법도 훌륭하지만, 저희는 개발 환경에 접근할 수 있는 IP가 제한되어 있어 husky를 사용하는 방식을 채택했습니다.

4. 병렬테스트

끔찍한 E2E 테스트 속도를 보완하기 위해서는 병렬 테스트가 필수라고 생각했습니다. 문제는 Cypress를 사용하고 싶었는데 Cypress는 병렬 테스트를 유료 플랜에서만 지원하는 것이죠. 다행히 우리는 Sorry-Cypress라는 걸출한 라이브러리를 발견해 해결했습니다. 간단하게 소개하자면 Sorry-Cypress에서 제공하는 도커 이미지를 로컬에 띄워 놓고, E2E 테스트 프로세스를 여러 개 동시에 실행하는 방식입니다. 동일한 id로 실행된 테스트를 Sorry-Cypress 로컬 서버에서 잘 분배해서 중복되지 않게 돌려줍니다. 물론 별도의 환경을 구축한다면 자체 서버를 띄울 수도 있고, 대시보드를 운영할 수도 있습니다.

Sorry-Cypress 도입으로 약 20개의 테스트 케이스 기준, 4개의 프로세스를 병렬 실행하여 2분 이상 걸리던 테스트를 30초대로 줄일 수 있었고, 추후 필요에 따라 프로세스를 늘리면 되니 이후의 테스트 코드도 부담 없이 작성할 수 있게 되었습니다. 이로써 마지막 걸림돌이던 테스트 속도 문제도 해결했습니다.

Sorry-Cypress 병렬 처리

Sorry-Cypress는 동일한 ID로 실행된 여러 테스트 프로세스를 분배해서 병렬로 처리합니다.

자세한 내용: Sorry-Cypress Parallelization Guide

걸림돌과 해결책 정리

정리하면, 기획서 기반 시나리오 작성으로 QA 하위호환 문제와 테스트 코드 관리 문제를, Sorry-Cypress 병렬 테스트로 테스트 속도 문제를 해결했습니다.

걸림돌해결책
QA 팀의 하위 호환기획서 기반 시나리오 작성으로 QA 시나리오와 동기화
테스트 코드 관리의 어려움기획서 업데이트 시 테스트 코드도 함께 업데이트
테스트 속도Sorry-Cypress를 활용한 병렬 테스트

도입 결과

테스트 도입 초기이지만 꽤 유의미한 결과를 만들어내고 있습니다. E2E 시나리오를 작성하면서 생각하지 못한 프론트엔드 버그를 종종 찾아내고 있으며, E2E 테스트 과정 중 API에서 발생한 사이드이펙트들을 잡아내는 경우도 있었습니다. Sorry-Cypress 도입으로 테스트 속도도 상당히 빨라져서 pre-push 과정에 도는 E2E 테스트가 크게 거슬린다는 느낌도 받지 못했습니다.

물론, 아직은 QA를 진행하지 않은 상황이라 개발자 단독으로 진행한 E2E 테스트가 QA 과정에서의 버그 발생률을 현저히 줄여주고 QA 기간을 단축할 수 있을지는 미지수입니다. 하지만 직접 겪어보지 않으면 모르는 일이고, 이런 경험들이 쌓여서 추후 QA 팀의 업무를 덜어주는 데 큰 역할을 할 수 있을 것이라는 확신이 생겼습니다.

QA와 별개로 기획서 기반의 E2E 테스트 시나리오 작성은 전체적인 프로젝트 흐름을 파악하는 데 큰 도움을 주고 있고, 테스트의 목적이 명확히 드러나는 점 또한 만족스럽습니다. E2E 테스트 이전에는 다른 분들의 코드를 수정할 때 상당히 부담스러운 점이 많았지만, 지금은 사이드이펙트를 잡아주는 E2E 테스트가 있어 한결 편하게 코드를 수정할 수 있게 되었습니다. 결과적으로 E2E 테스트의 도입은 만족스럽다고 말씀드리고 싶습니다.

// 기획서 내용으로 테스트코드 작성해보기
describe("사용자 로그인", () => {
it("이메일과 비밀번호를 입력하고 로그인 버튼을 클릭하면 메인 페이지로 이동한다", () => {
cy.visit("/login");
cy.get('[data-testid="email-input"]').type("user@example.com");
cy.get('[data-testid="password-input"]').type("password123");
cy.get('[data-testid="login-button"]').click();
cy.url().should("include", "/main");
});

it("잘못된 비밀번호를 입력하면 에러 메시지가 표시된다", () => {
cy.visit("/login");
cy.get('[data-testid="email-input"]').type("user@example.com");
cy.get('[data-testid="password-input"]').type("wrongpassword");
cy.get('[data-testid="login-button"]').click();
cy.get('[data-testid="error-message"]').should("be.visible");
});
});

맺음말

E2E 테스트의 도입은 생각 외로 엄청나게 간단합니다. 실제로 Cypress를 설치해 보시면 5분 이내로 프로젝트에 세팅이 끝나고 테스트 작성을 시작할 수 있습니다. 그러나 쉬운 도입에 비해 관리하기가 어려운 테스트라는 것은 많은 E2E 테스트 후기들이 증명해 줍니다. 제대로 관리되지 않는다면 시간을 잡아먹는 애물단지가 되어 있을 가능성이 크고, 어느새 프로젝트에서 E2E 테스트를 제거하는 PR을 올리고 있을지도 모릅니다.

하지만, E2E 테스트는 그만큼 가치 있고 매력적인 테스트라는 것은 분명합니다. 만약 여러분의 회사에 QA 팀이 없다면, 사용자 시나리오를 자동화된 방식으로 검증할 수 있는 유일한 방법이 될 수 있습니다. 혹은 QA 팀이 존재하더라도 적절한 협업을 통해 QA 팀의 업무 부하를 상당 부분 줄여줄 수 있는 선택지가 될 수도 있습니다. 여러분의 프로젝트에도 E2E 테스트가 필요할지, 필요하다면 어떤 식으로 도입하면 좋을지 선택하는데 이 글이 조금이라도 도움이 되었으면 좋겠습니다.

Reference