Cypress
JeongSeulho
2024년 01월 20일
준비중...
클립보드로 복사
0. 들어가며
Cypress
를 통한 E2E 테스트에 대해 정리
1. Cypress
아래는 백엔드를 테스트 서버로 실행하지 않고 로컬에서 실행하며, E2E 실행 또한 클라우드 환경이 아닌 로컬에서 진행하는 경우를 설명
(1) Cypress 설치
로컬에서 테스트할 경우 웹 앱을 먼저 실행하고 테스트를 진행해야 한다, 이때 start-server-and-test
를 사용하면 테스트 전에 서버를 실행하고 테스트를 진행이 보장된다.
npm i -D cypress
npm i start-server-and-test
(2) head 모드, headless 모드
-
head 모드
cypress open
명령어로 실행- 브라우저 및 UI 구동
- 디버깅 용이
-
headless 모드
cypress run
명령어로 실행- 브라우저 UI 없이 브라우저 엔진만 구동하고 CLI로 테스트 진행
- 클라우드 환경에서 테스트 진행 시 사용
(3) Time Travel 기능
E2E 테스트 과정 중 일어나는 모든 화면을 스냅샷으로 저장하여 히스토리를 확인할 수 있다.
2. Cypress로 E2E 테스트 작성
(1) cypress 설정 파일
// cypress.config.js
import { defineConfig } from 'cypress';
const baseUrl = 'http://localhost:5173';
export default defineConfig({
e2e: {
video: false,
viewportWidth: 1200,
viewportHeight: 1000,
baseUrl,
scrollBehavior: 'center', // selector를 선택했을 때 top으로 스크롤이 되는 문제가 발생. header 영역을 고려하기 위해서는 해당 설정이 필요
},
env: {
baseUrl,
},
});
(2) E2E 테스트 코드
cy.
으로 API를 사용.
또는then
을 사용한 체이닝 형태로 작성해야 함
- 내부적으로
Promise
를 사용하고 이를subject
객체라 부름 - 이러한 실행 결과 전달을
yield
라고 함 - 중간에 변수에 담아 사용할 수 없음
// cypress/e2e/Login.cy.js
beforeEach(() => {
// '/login' 페이지로 이동
cy.visit('/login');
});
it('이메일을 입력하지 않고 로그인 버튼을 클릭할 경우 "이메일을 입력하세요" 경고 메세지가 노출된다', () => {
cy.findByLabelText('로그인').click();
cy.findByText('이메일을 입력하세요').should('exist');
});
it('비밀번호를 입력하지 않고 로그인 버튼을 클릭할 경우 "비밀번호를 입력하세요" 경고 메세지가 노출된다', () => {
cy.findByLabelText('로그인').click();
cy.findByText('비밀번호를 입력하세요').should('exist');
});
it('잘못된 양식의 이메일을 입력한 뒤 로그인 버튼을 클릭할 경우 "이메일 양식이 올바르지 않습니다" 경고 메세지가 노출된다', () => {
cy.findByLabelText('이메일').type('wrongemail#mail.com');
cy.findByLabelText('로그인').click();
cy.findByText('이메일 양식이 올바르지 않습니다').should('exist');
});
it('회원 가입 클릭 시 회원 가입 페이지로 이동한다', () => {
cy.findByText('회원가입').click();
cy.url().should('eq', `${Cypress.env('baseUrl')}/register`);
});
it('성공적으로 로그인 되었을 경우 메인 홈 페이지로 이동하며, 사용자 이름 "Maria"와 장바구니 아이콘이 노출된다', () => {
const username = 'maria@mail.com';
const password = '12345';
cy.findByLabelText('이메일').type(username);
cy.findByLabelText('비밀번호').type(password);
cy.findByLabelText('로그인').click();
cy.url().should('eq', `${Cypress.env('baseUrl')}/`);
cy.findByText('Maria').should('exist');
cy.findByTestId('cart-icon').should('exist');
});
(3) 반복 쿼리 리팩토링
beforeEach(() => {
cy.visit('/login');
// 자주 되는 쿼리를 미리 가져옴
cy.findByLabelText('로그인').as('loginBtn');
});
it('이메일을 입력하지 않고 로그인 버튼을 클릭할 경우 "이메일을 입력하세요" 경고 메세지가 노출된다', () => {
// get을 통해 가져온 쿼리를 사용
cy.get('@loginBtn').click();
cy.findByText('이메일을 입력하세요').should('exist');
});
(4) then 사용
then
을 사용한 경우 콜백 함수 파라미터로JQuery
객체로 받으므로JQuery
메서드를 사용할 수 있다.
cypress subject
객체로 파라미터를 사용하려면wrap
을 사용해야 한다.
then
내부에서 아무것도 리턴하지 않으면 이전의subject
객체를 그대로 사용한다.
- 아래 예에서
then
내부에서 리턴이 없으므로findByLabelText
의subject
객체를 그대로 사용한다.
cy.findByLabelText('이메일')
.then(($email) => {
// JQuery 객체로 받아서 JQuery 메서드를 사용할 수 있다.
const cls = $email.attr('class');
// JQuery 객체를 subject 객체로 사용하려면 wrap을 사용해야 한다.
cy.wrap($email).click();
})
.click();
(5) Retry-ability
E2E 테스트 시에 API 응답을 기다리는 중이나 렌더링 중 등의 상황을 고려 하여 특정 시간동안 재시도를 하며 기본적으로 4초이다.
// 만약 렌더링이 오래걸리는 컴포넌트라 판단 되면 재시도를 10초로 늘린다.
cy.findByLabelText('이메일', { timeout: 10000 });
API 종류와 Retry-ability
- Query : 체이닝 로직을 재시도하며 실행 get, find 등
- Assertion : 단얼을 수행하는 쿼리 should
- Non-Query : 재시도하지 않고 한번만 실행 visit, click, then 등