E2E 테스트를 작성할 때 고려해야 할 사항 #frontend@twiliosendgrid

게시 됨: 2020-09-19

Twilio SendGrid에서는 모든 부분이 최종 사용자의 관점에서 프런트엔드와 백엔드 간에 연결되고 제대로 작동하는지 확인하기 위해 새로운 기능 또는 페이지 개발 주기가 끝날 때 종단 간(E2E) 테스트를 작성합니다.

우리는 자체 사용자 지정 Ruby Selenium 프레임워크, WebdriverIO 및 주로 Cypress와 같은 다양한 E2E 테스트 프레임워크 및 라이브러리를 2년 넘게 실험 했습니다 . 솔루션. 우리가 사용한 프레임워크나 라이브러리에 관계없이 E2E 테스트를 자동화하고 작성할 수 있는 기능에 대해 동일한 질문을 하고 있다는 사실을 알게 되었습니다. 어떤 기능을 테스트할 수 있는지 확인한 후 테스트를 작성하고 설정하기 위해 동일한 일반 전략을 반복해서 적용하는 자신도 발견했습니다.

이 블로그 게시물은 특정 라이브러리나 프레임워크에서 E2E 테스트를 작성하는 데 대한 사전 지식이 필요하지 않지만 웹 애플리케이션을 보고 페이지가 올바르게 작동하는지 테스트하기 위해 브라우저에서 작업을 가장 잘 자동화하는 방법에 대해 궁금해했다면 도움이 됩니다. E2E 테스트에 대해 생각하는 방법을 안내하는 것을 목표로 하므로 이러한 질문과 테스트 작성을 위한 일반 전략을 선택한 프레임워크에 적용할 수 있습니다.

E2E 테스트를 자동화할 수 있는지 묻는 질문

E2E 테스트를 작성할 때 애플리케이션에서 테스트하는 페이지의 흐름이 특정 기준을 충족하는지 확인해야 합니다. E2E 테스트를 자동화할 수 있는지 결정하기 위해 스스로에게 묻는 몇 가지 상위 수준 질문을 살펴보겠습니다.

1. API와 같은 신뢰할 수 있는 방법을 통해 각 테스트 전에 사용자 데이터를 특정 상태로 재설정할 수 있습니까? 사용자를 원하는 상태로 안정적으로 재설정할 방법이 없는 경우 자동화할 수 없으며 배포 전에 차단 테스트의 일부로 실행될 것으로 예상됩니다. 또한 느리고 UI를 통한 자동화 단계가 이미 충분히 불안정하기 때문에 UI를 통해 사용자를 특정 상태로 되돌리는 것은 반패턴이고 일반적으로 비결정적입니다. 브라우저에서 페이지를 열 필요 없이 API를 호출하여 사용자 상태를 재설정하는 것이 더 안정적입니다. 서비스가 있는 경우 다른 대안은 각 테스트 전에 적절한 데이터로 새 사용자를 만드는 것입니다. 각 테스트 전에 지속 사용자를 재설정하거나 사용자를 생성하는 한 페이지에서 테스트 중인 부분에 집중할 수 있습니다.

2. 테스트하려는 기능, API 또는 시스템을 제어할 수 있습니까? 청구 또는 기타 기능을 위해 의존하는 타사 서비스인 경우 이를 조롱하거나 특정 값으로 결정적으로 작동하도록 하는 방법이 있습니까? 벗겨짐을 줄이기 위해 테스트를 최대한 제어하려고 합니다. 테스트 실행당 리소스 또는 데이터가 격리된 전용 테스트 사용자를 생성하여 다른 것에 영향을 받지 않도록 할 수 있습니다.

3. 서비스 또는 기능 자체가 합리적인 제한 시간 내에 작동할 수 있을 만큼 일관성이 있습니까? 종종 폴링을 구현하거나 특정 데이터가 처리되어 데이터베이스에 전달될 때까지 기다려야 할 수 있습니다(예: 느린 비동기 업데이트 및 트리거된 이메일 이벤트). 이러한 서비스가 합리적이고 안정적인 시간 내에 자주 발생하는 경우 특정 DOM 요소가 표시되거나 데이터가 업데이트될 때까지 기다릴 때 적절한 폴링 시간 초과를 설정할 수 있습니다.

4. 페이지에서 상호 작용해야 하는 요소를 선택할 수 있습니까? 제어할 수 없고 변경할 수 없는 iframe 또는 생성된 요소를 처리하고 있습니까? 페이지의 요소와 상호 작용하기 위해 id 또는 클래스 이름을 선택하는 대신 `data-hook` 또는 `data-testid` 속성과 같은 보다 구체적인 선택기를 추가할 수 있습니다. ID와 클래스 이름은 일반적으로 스타일과 연관되기 때문에 변경되기 쉽습니다. 그렇지 않으면 스타일이 지정된 구성 요소 또는 CSS 모듈에서 해시된 클래스 이름 또는 ID를 선택하려고 한다고 상상해 보십시오. 제3자 생성 요소 또는 react-select와 같은 오픈 소스 구성 요소 라이브러리의 경우 'data-hook' 속성이 있는 상위 요소로 해당 요소를 래핑하고 그 아래에 있는 하위 요소를 선택할 수 있습니다. iframe을 다루기 위해 우리는 어설션 및 작업에 필요한 DOM 요소를 추출하는 사용자 지정 명령을 만들었습니다. 나중에 예제를 제공할 것입니다.

고려해야 할 사항이 더 있지만 모두 한 가지 질문으로 귀결됩니다. 이 E2E 테스트를 일관되고 시기 적절한 방식으로 반복하여 동일한 결과를 얻을 수 있습니까?

E2E 테스트 작성을 위한 일반 전략

1. 자동화할 수 있는 높은 가치의 테스트 사례를 파악합니다 . 몇 가지 예에는 대부분의 기능 흐름을 다루는 행복한 경로 테스트가 포함됩니다. UI를 통해 사용자 프로필 정보에 대한 CRUD 작업 수행, 일부 데이터가 제공된 결과 일치를 위해 테이블 ​​필터링, 게시물 작성 또는 API 키 설정. 그러나 다른 에지 케이스 및 오류 처리는 단위 및 통합 테스트로 다루는 것이 더 나을 수 있습니다. 테스트 케이스 목록을 줄이는 데 도움이 되도록 이전 섹션에서 언급한 질문을 통해 실행하십시오.

2. API를 최대한 많이 설정하거나 분해하여 이러한 테스트를 반복하는 방법을 생각하십시오. 가치가 높고 자동화 가능한 테스트 사례의 경우 API를 통해 설정해야 할 사항을 기록하기 시작합니다. 몇 가지 예는 사용자에게 페이지 매김을 위한 필터링 가능한 데이터가 충분하지 않은 경우, 사용자의 데이터가 30일의 롤링 기간에 만료되는 경우 또는 성공 또는 불완전한 데이터에서 남은 일부 데이터를 분해해야 하는 경우 적절한 데이터를 사용자에게 시드하는 것입니다. 현재 테스트가 다시 시작되기 전에 테스트합니다. 테스트는 마지막 테스트 실행의 성공 여부에 관계없이 동일한 반복 가능한 상태로 실행 및 설정될 수 있어야 합니다.

생각하는 것이 중요합니다. 원하는 기능의 일부만 테스트할 수 있도록 이 사용자의 데이터를 다시 시작점으로 재설정하려면 어떻게 해야 합니까?

예를 들어, 사용자가 게시물을 추가하여 결국 사용자의 게시물 목록에 표시되도록 하려면 해당 게시물을 먼저 삭제해야 합니다.

3. 고객 입장에서 기능 흐름을 완전히 완료하는 데 필요한 UI 단계를 추적합니다. 고객이 전체 흐름 또는 작업을 완료하는 단계를 기록합니다. 각 단계 후에 사용자가 보거나 상호 작용해야 하거나 하지 말아야 하는 내용을 추적합니다. 우리는 사용자가 자신의 작업에 대해 적절한 이벤트 시퀀스를 접할 수 있도록 온전성 검사 및 어설션을 수행할 것입니다. 그런 다음 온전성 검사를 자동화된 명령 및 어설션으로 변환합니다.

4. 특정 선택기를 추가하고 페이지 개체(또는 다른 종류의 래퍼)를 구현하여 변경 사항을 유지하고 흐름을 자동화합니다. 기능 흐름을 조작하고 진행하는 방법에 대해 적어 둔 단계를 검토하십시오. 버튼, 모달, 입력, 테이블 행, 경고 및 카드와 같이 사용자가 상호 작용하는 요소에 `data-hook` 속성과 같은 보다 구체적인 선택기를 추가합니다. 원하는 경우 추가한 선택기를 통해 해당 요소에 대한 참조가 있는 페이지 개체, 래퍼 또는 도우미 파일을 만들 수 있습니다. 그런 다음 재사용 가능한 기능을 구현하여 페이지의 실행 가능한 요소와 상호 작용할 수 있습니다.

5. 작성한 도우미를 사용하여 녹음한 사용자 단계를 Cypress 테스트로 변환합니다. 테스트에서 우리는 일반적으로 API를 통해 사용자에게 로그인하고 로그인을 유지하기 위해 각 테스트 케이스가 실행되기 전에 세션 쿠키를 보존합니다. 그런 다음 일관된 시작 지점을 갖도록 API를 통해 사용자 데이터를 설정하거나 해제합니다. 모든 것이 준비되면 기능에 대해 직접 테스트할 페이지를 방문합니다. 흐름 생성, 업데이트 또는 삭제와 같은 흐름에 대한 실행 단계를 진행하여 진행 과정에서 페이지에 어떤 일이 발생하거나 표시되어야 하는지 주장합니다. 테스트 속도를 높이고 불안정함을 줄이려면 UI를 통해 상태를 재설정하거나 구축하는 것을 피하고 로그인 페이지를 통해 로그인하거나 UI를 통해 항목을 삭제하여 테스트하려는 부분에 집중하는 등의 작업을 건너뛰십시오. 항상 `before` 또는 `beforeEach` 후크에서 이러한 부분을 수행해야 합니다. 그렇지 않고 `after` 또는 `afterEach` 후크를 사용한 경우 테스트가 중간에 실패하여 정리 단계가 실행되지 않고 후속 테스트 실행이 실패할 수 있습니다.

6. 망치질을 하고 테스트 박편을 제거합니다. 테스트를 구현하고 로컬에서 몇 번 통과한 후에는 pull 요청을 설정하고 즉시 병합하고 나머지 테스트 제품군과 일정에 따라 테스트를 실행하거나 배포 단계에서 트리거하고 싶을 수 있습니다. 그렇게 하기 전에:

    1. 먼저 사용자를 다양한 상태로 두고 테스트가 여전히 통과하는지 확인하여 적절한 설정 단계가 있는지 확인합니다.
    2. 다음으로 배포 흐름 중 하나가 실행되는 동안 테스트를 병렬로 실행하는 방법을 조사합니다. 이를 통해 동일한 사용자가 리소스를 짓밟고 있는지, 경쟁 조건이 발생하는지 확인할 수 있습니다.
    3. 그런 다음 테스트가 Docker 컨테이너에서 헤드리스 모드로 실행되는 방식을 관찰하여 시간 초과를 늘리거나 선택기를 조정해야 하는지 확인합니다.

목표는 다양한 조건에서 반복되는 테스트 실행에서 테스트가 어떻게 작동하는지 확인하고 테스트를 가능한 한 안정적이고 일관되게 만들어 테스트를 수정하는 데 더 적은 시간을 할애하고 환경에서 실제 버그를 잡는 데 더 집중하는 것입니다.

다음은 'cy.login(username, password)'이라는 로그인 전역 지원 명령을 생성한 샘플 Cypress 테스트 상용구 레이아웃입니다. 쿠키를 명시적으로 설정하고 각 테스트 케이스 전에 보관하므로 로그인 상태를 유지하고 바로 이동할 수 있습니다. 테스트 중인 페이지로 이동합니다. 또한 API를 통해 일부 설정 또는 해제를 수행하고 아래와 같이 매번 로그인 페이지를 우회합니다.

마무리 생각

어떤 E2E 솔루션을 사용하는 것이 가장 좋은지 비교하는 것 외에도 E2E 테스트에 대한 올바른 사고 방식을 채택하는 것도 중요합니다. 테스트하려는 기능이 자동화 요구 사항에 맞는지 여부에 대해 먼저 질문하는 것이 중요합니다. 유효성을 검사하려는 항목에 집중할 수 있도록 사용자 또는 데이터를 특정 상태로 안정적으로 재설정하는 방법(예: API를 통해)이 있어야 합니다.

사용자 또는 데이터를 적절한 시작점으로 재설정하는 신뢰할 수 있는 방법이 없는 경우 특정 구성으로 사용자를 생성하기 위한 빌드 도구 및 API를 조사해야 합니다. 테스트를 가능한 한 안정적이고 일관성 있게 만들기 위해 제어할 수 있는 항목을 조롱하는 것도 고려할 수 있습니다. 그렇지 않으면 팀과 함께 가치와 절충안을 고려해야 합니다. 이 기능은 새 코드 변경 사항이 푸시될 때 단위 테스트 또는 수동 회귀 테스트에 맡겨야 하는 기능입니까?

E2E 테스트에서 자동화할 수 있는 기능의 경우 사용자의 주요 행복 경로 흐름을 다루는 것이 가장 가치 있는 경우가 많습니다. 다시 한 번, 원하는 프레임워크나 라이브러리로 E2E 테스트를 작성할 때 시기 적절하고 일관된 방식으로 반복 가능하게 만들고 취약성을 없애십시오.

특히 Cypress E2E 테스트에 대한 자세한 내용은 다음 리소스를 확인하십시오.

  • Cypress 테스트 작성에 대한 1,000피트 개요
  • Cypress 테스트의 모든 것을 TypeScript
  • Cypress 테스트에서 이메일 흐름 다루기
  • Cypress 테스트 구성, 구성 및 통합을 위한 아이디어
  • Docker, Buildkite 및 CICD와 Cypress 테스트 통합