Cypress 테스트 구성, 구성 및 통합을 위한 아이디어 #frontend@twiliosendgrid
게시 됨: 2020-12-15여기 Twilio SendGrid에서 다양한 웹 애플리케이션과 프런트엔드 팀에 걸쳐 많은 Cypress 테스트를 작성했습니다. 테스트가 많은 기능과 페이지로 확장됨에 따라 몇 가지 유용한 구성 옵션을 발견했고 점점 더 많은 파일, 페이지 요소에 대한 선택기 및 시간 초과 값을 더 잘 유지 관리하는 방법을 개발했습니다.
우리는 Cypress 관련 항목을 구성, 구성 및 통합하기 위한 이러한 팁과 아이디어를 보여 주는 것을 목표로 하므로 귀하와 귀하의 팀에 가장 적합한 방법을 자유롭게 사용하십시오.
이 블로그 게시물은 귀하가 Cypress 테스트에 대한 작업 지식을 어느 정도 가지고 있고 Cypress 테스트를 더 잘 유지 관리하고 개선하기 위한 아이디어를 찾고 있다고 가정합니다. 그러나 별도의 환경에 대해 Cypress 테스트를 작성하는 데 유용할 수 있는 일반적인 기능, 주장 및 패턴에 대해 자세히 알아보려면 대신 이 천 피트 개요 블로그 게시물을 확인하세요.
그렇지 않으면 계속해서 Cypress에 대한 몇 가지 구성 팁을 먼저 안내해 드리겠습니다.
cypress.json 구성
cypress.json 파일은 Cypress 명령, 환경 변수 및 기타 속성에 대한 기본 시간 초과와 같은 cypress.json
테스트에 대한 모든 구성을 설정할 수 있는 곳 입니다.
다음은 구성에서 시도해 볼 수 있는 몇 가지 팁입니다.
- 기본 명령 시간 초과를 미세 조정하여 Cypress 명령 전체에 항상
{ timeout: timeoutInMs }
를 추가할 필요가 없습니다. "defaultCommandTimeout", "requestTimeout" 및 "responseTimeout"과 같은 설정에 대한 올바른 균형을 찾을 때까지 숫자를 수정하십시오. - 테스트에 iframe이 포함된 경우 "chromeWebSecurity"를 false로 설정해야 애플리케이션에서 원본 간 iframe에 액세스할 수 있습니다.
- Cypress 테스트를 실행할 때 타사 마케팅, 분석 및 로깅 스크립트를 차단하여 속도를 높이고 원치 않는 이벤트를 트리거하지 마십시오. "blacklistHosts" 속성(또는 Cypress 5.0.0의 "blockHosts" 속성)을 사용하여 거부 목록을 쉽게 설정할 수 있으며, 타사 호스트 경로와 일치하는 문자열 globs의 배열을 사용할 수 있습니다.
- Cypress GUI를 열 때 "viewportWidth" 및 "viewportHeight"를 사용하여 기본 뷰포트 치수를 조정하여 보다 쉽게 볼 수 있습니다.
- 중요한 테스트를 위해 Docker에 메모리 문제가 있거나 작업을 더 효율적으로 만들고 싶다면 "numTestsKeptInMemory"를 기본값보다 작은 숫자로 수정하고 "videoUploadOnPasses"를 false로 설정하여 실패한 테스트에 대한 비디오 업로드에 집중할 수 있습니다. 만 실행됩니다.
또 다른 참고 사항으로, Cypress 구성을 조정한 후 TypeScript를 추가하고 이 블로그 게시물에서 했던 것처럼 Cypress 테스트를 더 잘 입력할 수도 있습니다. 이는 cy.task('someTaskPluginFunction)
, Cypress.env('someEnvVariable')
또는 cy.customCommand()
와 같은 함수를 호출하고 연결할 때 자동 완성, 유형 경고 또는 오류에 특히 유용합니다.
또한 기본 cypress.json
파일을 설정하고 package.json
에서 다른 Cypress 스크립트를 실행할 때 staging.json
과 같은 각 테스트 환경에 대해 별도의 Cypress 구성 파일을 로드하는 실험을 할 수 있습니다. Cypress 문서는 이 접근 방식을 잘 안내합니다.
Cypress 폴더 정리
Cypress는 코드베이스에서 Cypress를 처음 시작할 때 이미 안내된 구조로 최상위 cypress
폴더를 설정합니다. 여기에는 사양 파일에 대한 integration
, JSON 데이터 파일에 대한 fixtures
, cy.task(...)
기능 및 기타 구성을 위한 plugins
, 사용자 지정 명령 및 유형에 대한 support
폴더와 같은 기타 폴더가 포함됩니다.
Cypress 폴더, React 구성 요소 또는 일반적으로 모든 코드 내에서 구성할 때 따라야 할 좋은 경험 법칙은 함께 변경될 가능성이 있는 항목을 함께 배치하는 것입니다. 이 경우 브라우저에서 웹 응용 프로그램을 다루기 때문에 확장성이 좋은 한 가지 접근 방식은 페이지 또는 가장 중요한 기능별로 폴더의 항목을 그룹화하는 것입니다.
"DomainAuthentication"(/settings/sender_auth/domains/**/*) 및 "LinkBranding"과 같은 /settings/sender_auth
경로 아래에 모든 페이지 개체를 보관하기 위해 "SenderAuthentication"과 같은 기능 이름으로 분할된 별도의 pages
폴더를 만들었습니다. (/settings/sender_auth/links/**/*). plugins
폴더에서 특정 기능 또는 페이지에 대한 모든 cy.task(...)
플러그인 파일을 보낸 사람 인증을 위한 동일한 폴더에 구성하는 것과 동일한 작업을 수행했으며 integration
폴더에 대해서도 동일한 접근 방식을 따랐습니다. 사양 파일. 테스트를 수백 개의 사양 파일, 페이지 개체, 플러그인 및 코드베이스 중 하나의 기타 파일로 확장했으며 쉽고 편리하게 탐색할 수 있습니다.
다음은 cypress
폴더를 구성한 방법에 대한 개요입니다.
integration
폴더(모든 사양이 있는 위치)를 구성할 때 고려해야 할 또 다른 사항은 잠재적으로 테스트의 우선 순위에 따라 사양 파일을 분할하는 것입니다. 예를 들어, "P1" 폴더에 모든 가장 높은 우선순위 및 값 테스트를 갖고 "P2" 폴더에 두 번째 우선순위 테스트를 가질 수 있으므로 다음과 같이 --spec
옵션을 설정하여 모든 "P1" 테스트를 쉽게 실행할 수 있습니다 --spec 'cypress/integration/P1/**/*'
.
--spec 'cypress/integration/SomePage/**/*'
와 같은 페이지 또는 기능별로 뿐만 아니라 우선 순위, 제품 , 또는 환경.
요소 선택기 통합
페이지의 React 구성 요소를 개발할 때 일반적으로 Jest 및 Enzyme을 사용하여 일정 수준의 단위 및 통합 테스트를 추가합니다. 기능 개발이 끝나갈 무렵 Cypress E2E 테스트의 또 다른 계층을 추가하여 모든 것이 백엔드에서 작동하는지 확인합니다. React 구성 요소에 대한 단위 테스트와 Cypress E2E 테스트에 대한 페이지 개체 모두 상호 작용하고 주장하려는 페이지의 구성 요소/DOM 요소에 대한 선택기가 필요합니다.
이러한 페이지와 구성 요소를 업데이트할 때 단위 테스트 선택기에서 Cypress 페이지 개체, 실제 구성 요소 코드 자체에 이르기까지 여러 위치를 동기화해야 하므로 드리프트와 오류가 발생할 수 있습니다. 스타일과 관련된 클래스 이름에만 의존한다면 깨질 수 있는 모든 위치를 업데이트하는 것을 기억하는 것이 고통스러울 것입니다. 대신 "data-hook", "data-testid" 또는 일관되게 명명된 "data-*" 속성을 페이지의 특정 구성 요소 및 요소에 추가하고 테스트 레이어에서 이에 대한 선택기를 작성하고 기억하고 싶습니다.
우리는 많은 요소에 "data-hook" 속성을 추가할 수 있지만 다른 파일에서 업데이트하고 재사용하기 위해 모든 요소를 한 곳에서 그룹화하는 방법이 여전히 필요했습니다. 우리는 이 모든 "data-hook" 선택자를 React 구성 요소에 배포하고 단위 테스트 및 페이지 개체 선택자에서 활용하여 내보낸 개체에서 더 많은 재사용과 더 쉬운 유지 관리를 위해 유형이 지정된 방법을 알아냈습니다.
각 페이지의 최상위 폴더에 대해 요소에 대한 읽을 수 있는 키 이름과 요소에 대한 실제 문자열 CSS 선택기를 값으로 사용하여 개체를 관리하고 내보내는 hooks.ts
파일을 만듭니다. 단위 테스트에서 Enzyme의 wrapper.find(“[data-hook='selector']”)
또는 Cypress의 cy.get(“[data-hook='selector']”)
페이지 개체에 있습니다. 이러한 호출은 wrapper.find(Selectors.someElement)
또는 cy.get(Selectors.someElement)
) 처럼 더 깨끗해 보입니다.
다음 코드 스니펫은 이러한 읽기 선택기를 실제로 사용하는 이유와 방법에 대해 자세히 설명합니다.
마찬가지로 요소에 대해 읽을 수 있는 키 이름과 값으로 { “data-hook”: “selector” }
와 같은 개체를 사용하여 개체도 내보냅니다. 기본 요소에 "data-hook" 속성을 성공적으로 추가하기 위해 이러한 객체를 React 구성 요소에 props로 쓰거나 확산해야 하므로 이를 "Write Selectors"라고 부를 것입니다. 목표는 다음과 같이 하는 것입니다. props가 JSX 요소로 올바르게 전달되었다고 가정하면 아래의 실제 DOM 요소에도 "data-hook=" 속성이 설정됩니다.
다음 코드 스니펫은 이러한 쓰기 선택기를 실제로 사용하는 이유와 방법에 대해 자세히 설명합니다.
읽기 선택기를 통합하는 개체를 만들고 더 적은 수의 위치를 업데이트하기 위해 선택기를 쓸 수 있지만 더 복잡한 일부 페이지에 대해 많은 선택기를 작성해야 한다면 어떻게 될까요? 이것은 스스로 작성하기에는 더 오류가 발생하기 쉬우므로 이러한 읽기 선택기를 쉽게 생성하고 결국 특정 페이지에 대해 내보낼 쓰기 선택기를 생성하는 함수를 만들어 보겠습니다.
읽기 선택기 생성기 기능의 경우 입력 개체의 속성을 반복하고 각 요소 이름에 대해 [data-hook=”selector”]
CSS 선택기 문자열을 형성합니다. 키의 해당 값이 null
입력 개체의 요소 이름이 { someElement: null } => { someElement: '[data-hook=”someElement”] }
와 같은 "data-hook" 값과 동일하다고 가정합니다. { someElement: null } => { someElement: '[data-hook=”someElement”] }
. 그렇지 않고 키의 해당 값이 null이 아닌 경우 { someElement: “newSelector” } => { someElement: '[data-hook=”newSelector”]' }
와 같은 "data-hook" 값을 재정의하도록 선택할 수 있습니다.
쓰기 선택기 생성기 함수의 경우 입력 개체의 속성을 반복하고 각 요소 이름에 대해 { “data-hook”: “selector” }
개체를 형성합니다. 키의 해당 값이 null
입력 개체의 요소 이름이 { someElement: null } => { someElement: { “data-hook”: “someElement” } }
. 그렇지 않고 키의 해당 값이 null
이 아닌 경우 { someElement: “newSelector” } => { someElement: { “data-hook”: “newSelector” }' }
와 같은 "data-hook" 값을 재정의하도록 선택할 수 있습니다. .
이 두 개의 생성기 기능을 사용하여 읽기 선택기 및 페이지에 대한 쓰기 선택기 개체를 만들고 단위 테스트 및 Cypress 페이지 개체에서 재사용할 수 있도록 내보냅니다. 또 다른 보너스는 TypeScript 파일에서도 실수로 Selectors.unknownElement
또는 WriteSelectors.unknownElement
를 수행할 수 없는 방식으로 이러한 개체가 입력된다는 것입니다. 읽기 선택기를 내보내기 전에 제어할 수 없는 타사 구성 요소에 대한 추가 요소 및 CSS 선택기 매핑을 추가할 수도 있습니다. 어떤 경우에는 특정 요소에 "data-hook" 속성을 추가할 수 없으므로 여전히 다른 속성, ID 및 클래스로 선택하고 아래와 같이 읽기 선택기 개체에 더 많은 속성을 추가해야 합니다.
이 패턴은 페이지에 대한 모든 선택자와 업데이트해야 할 시기에 대해 정리된 상태를 유지하는 데 도움이 됩니다. 이러한 모든 선택기를 일종의 개체로 관리하거나 다른 수단을 통해 향후 변경 사항에 대해 수정해야 하는 파일 수를 최소화하는 방법을 조사하는 것이 좋습니다.
시간 초과 통합
많은 Cypress 테스트를 작성한 후 우리가 알아차린 한 가지는 특정 요소에 대한 선택기가 "defaultCommandTimeout" 및 "responseTimeout"과 같이 cypress.json
에 설정된 기본 시간 초과 값보다 시간 초과되고 더 오래 걸린다는 것입니다. 우리는 돌아가서 더 긴 시간 초과가 필요한 특정 페이지를 조정했지만 시간이 지남에 따라 임의의 시간 초과 값의 수가 증가했고 대규모 변경에 대해 이를 유지하는 것이 더 어려워졌습니다.
결과적으로 cy.get(...)
또는 cy.contains(...)
. 기본 시간 초과 외에도 cy.get(“someElement”, { timeout: timeouts.short })
와 같은 Cypress 명령에서 사용하기 위해 파일의 아무 곳이나 가져올 수 있는 시간 초과 개체 내에서 "short", "medium", "long", "xlong" 및 "xxlong"으로 확장합니다. cy.get(“someElement”, { timeout: timeouts.short })
또는 cy.task('pluginName', {}, { timeout: timeouts.xlong })
. 시간 초과를 가져온 개체의 이러한 값으로 교체한 후 특정 시간 초과에 걸리는 시간을 확장하거나 축소하기 위해 업데이트할 한 곳이 있습니다.
이 작업을 쉽게 시작할 수 있는 방법의 예가 아래에 나와 있습니다.
마무리
Cypress 테스트 제품군이 성장함에 따라 Cypress 테스트를 가장 잘 확장하고 유지 관리하는 방법을 찾을 때와 동일한 문제가 발생할 수 있습니다. 페이지, 기능 또는 기타 그룹화 규칙에 따라 파일을 구성하도록 선택할 수 있으므로 더 많은 개발자가 코드베이스에 기여함에 따라 기존 테스트 파일을 찾을 위치와 새 파일을 추가할 위치를 항상 알 수 있습니다.
UI가 변경되면 일종의 유형이 지정된 개체(예: 읽기 및 쓰기 선택기)를 사용하여 "data-" 속성 선택기를 단위 테스트 및 Cypress 내에서 주장하거나 상호 작용하려는 각 페이지의 주요 요소에 유지할 수 있습니다. 테스트. Cypress 명령에 대한 시간 초과 값과 같은 항목에 대해 너무 많은 임의 값을 적용하기 시작했다면 스케일링된 값으로 채워진 개체를 설정하여 한 곳에서 해당 값을 업데이트할 수 있습니다.
프론트엔드 UI, 백엔드 API 및 Cypress 테스트에서 상황이 변경되면 이러한 테스트를 더 쉽게 유지 관리할 수 있는 방법에 대해 항상 생각해야 합니다. 많은 개발자가 새로운 페이지, 기능 및 (필연적으로) Cypress 테스트를 향후에 추가함에 따라 업데이트하고 실수할 장소가 줄어들고 항목을 둘 위치에 대한 모호성이 줄어들면 큰 차이가 발생합니다.
Cypress에 대한 더 많은 게시물에 관심이 있으십니까? 다음 기사를 살펴보십시오.
- E2E 테스트를 작성할 때 고려해야 할 사항
- Cypress 테스트 작성에 대한 1,000피트 개요
- Cypress 테스트의 모든 것을 TypeScript
- Cypress 테스트에서 이메일 흐름 다루기
- Docker, Buildkite 및 CICD와 Cypress 테스트 통합