E2E Testing 완벽 마스터
E2E Testing의 핵심 개념과 실전 활용법
학습 항목
이미지 로딩 중...
Cypress 실무 활용 팁
Cypress를 실무 프로젝트에서 효과적으로 활용하기 위한 핵심 팁들을 소개합니다. 테스트 안정성 향상, 재사용 가능한 커맨드 작성, API 목킹 등 실전에서 바로 적용할 수 있는 베스트 프랙티스를 다룹니다.
들어가며
이 글에서는 Cypress 실무 활용 팁에 대해 상세히 알아보겠습니다. 총 12가지 주요 개념을 다루며, 각각의 개념에 대한 설명과 실제 코드 예제를 함께 제공합니다.
목차
- Custom_Commands로_재사용성_높이기
- Data_Attribute로_안정적인_선택자_사용
- cy.intercept로_API_응답_목킹
- fixture를_활용한_테스트_데이터_관리
- 조건부_테스트_처리하기
- 환경_변수로_다양한_환경_테스트
- before와_beforeEach로_효율적인_설정
- 암시적_대기로_비동기_처리
- 파일_업로드_테스트하기
- 페이지_객체_패턴으로_구조화
- 실패_시_스크린샷과_비디오_활용
- 테스트_격리로_독립성_보장
1. Custom_Commands로_재사용성_높이기
개요
반복되는 테스트 로직을 커스텀 커맨드로 만들어 코드 중복을 줄이고 유지보수성을 높일 수 있습니다.
코드 예제
// cypress/support/commands.js
Cypress.Commands.add('login', (email, password) => {
cy.visit('/login')
cy.get('[data-testid="email"]').type(email)
cy.get('[data-testid="password"]').type(password)
cy.get('[data-testid="submit"]').click()
})
// 테스트에서 사용
cy.login('user@test.com', 'password123')
설명
cy.login() 커맨드를 만들어 모든 테스트에서 간단하게 로그인 과정을 재사용할 수 있습니다.
2. Data_Attribute로_안정적인_선택자_사용
개요
CSS 클래스나 ID 대신 data-testid 속성을 사용하면 UI 변경에도 테스트가 깨지지 않습니다.
코드 예제
// ❌ 불안정한 방법
cy.get('.btn-primary').click()
// ✅ 안정적인 방법
cy.get('[data-testid="submit-button"]').click()
// ✅ 더 간결한 방법
cy.get('[data-cy="submit-button"]').click()
설명
data-testid나 data-cy 속성은 테스트 전용 선택자로, 스타일 변경에 영향받지 않아 테스트가 안정적입니다.
3. cy.intercept로_API_응답_목킹
개요
실제 API 호출 없이 네트워크 요청을 가로채서 원하는 응답을 반환할 수 있습니다.
코드 예제
cy.intercept('GET', '/api/users', {
statusCode: 200,
body: [
{ id: 1, name: 'John Doe' },
{ id: 2, name: 'Jane Smith' }
]
}).as('getUsers')
cy.visit('/users')
cy.wait('@getUsers')
설명
cy.intercept로 API 응답을 제어하여 다양한 시나리오를 테스트하고 백엔드 의존성을 제거합니다.
4. fixture를_활용한_테스트_데이터_관리
개요
테스트 데이터를 별도 파일로 관리하여 테스트 코드를 깔끔하게 유지할 수 있습니다.
코드 예제
// cypress/fixtures/users.json
{
"admin": { "email": "admin@test.com", "password": "admin123" },
"user": { "email": "user@test.com", "password": "user123" }
}
// 테스트에서 사용
cy.fixture('users').then((users) => {
cy.login(users.admin.email, users.admin.password)
})
설명
fixture 파일로 테스트 데이터를 중앙화하여 여러 테스트에서 일관되게 사용할 수 있습니다.
5. 조건부_테스트_처리하기
개요
요소의 존재 여부에 따라 다르게 동작하는 조건부 로직을 안전하게 구현할 수 있습니다.
코드 예제
cy.get('body').then($body => {
if ($body.find('[data-cy="modal"]').length > 0) {
cy.get('[data-cy="close-modal"]').click()
}
})
// 또는 should로 확인
cy.get('[data-cy="notification"]')
.should('exist')
.and('be.visible')
설명
jQuery 메서드를 사용하여 요소 존재를 확인하고, 조건에 따라 다른 액션을 수행할 수 있습니다.
6. 환경_변수로_다양한_환경_테스트
개요
cypress.config.js에서 환경 변수를 설정하여 개발, 스테이징, 프로덕션 환경을 쉽게 전환할 수 있습니다.
코드 예제
// cypress.config.js
module.exports = {
env: {
apiUrl: 'https://api.dev.example.com',
timeout: 10000
}
}
// 테스트에서 사용
cy.visit(Cypress.env('apiUrl') + '/users')
cy.wait(Cypress.env('timeout'))
설명
env 설정으로 환경별 URL이나 설정값을 관리하여 유연하게 테스트를 실행할 수 있습니다.
7. before와_beforeEach로_효율적인_설정
개요
테스트 전에 공통 설정을 수행하여 테스트 코드의 중복을 줄이고 실행 속도를 높일 수 있습니다.
코드 예제
describe('User Dashboard', () => {
before(() => {
cy.clearCookies()
})
beforeEach(() => {
cy.login('user@test.com', 'password')
cy.visit('/dashboard')
})
it('shows user profile', () => {
cy.get('[data-cy="profile"]').should('be.visible')
})
})
설명
before는 한 번만, beforeEach는 각 테스트마다 실행되어 테스트 환경을 효율적으로 준비합니다.
8. 암시적_대기로_비동기_처리
개요
Cypress는 자동으로 요소를 기다리지만, cy.wait()를 명시적으로 사용하여 특정 조건을 기다릴 수 있습니다.
코드 예제
// API 요청 기다리기
cy.intercept('POST', '/api/save').as('saveData')
cy.get('[data-cy="save-btn"]').click()
cy.wait('@saveData')
cy.contains('저장되었습니다').should('be.visible')
// 시간 기반 대기 (비추천)
// cy.wait(3000)
설명
네트워크 요청에 별칭을 지정하고 cy.wait()로 완료를 기다려 테스트 안정성을 높입니다.
9. 파일_업로드_테스트하기
개요
cypress-file-upload 플러그인을 사용하여 파일 업로드 기능을 간단하게 테스트할 수 있습니다.
코드 예제
// cypress/support/commands.js에서 설정
import 'cypress-file-upload'
// 테스트에서 사용
cy.get('[data-cy="file-input"]')
.attachFile('example.jpg')
cy.get('[data-cy="upload-btn"]').click()
cy.contains('업로드 완료').should('be.visible')
설명
attachFile 커맨드로 fixtures 폴더의 파일을 input 요소에 첨부하여 업로드를 테스트합니다.
10. 페이지_객체_패턴으로_구조화
개요
페이지별로 선택자와 액션을 클래스로 캡슐화하여 테스트 코드의 가독성과 유지보수성을 높입니다.
코드 예제
// cypress/pages/LoginPage.js
class LoginPage {
visit() { cy.visit('/login') }
fillEmail(email) { cy.get('[data-cy="email"]').type(email) }
fillPassword(pwd) { cy.get('[data-cy="password"]').type(pwd) }
submit() { cy.get('[data-cy="submit"]').click() }
}
export default new LoginPage()
// 테스트에서 사용
import LoginPage from '../pages/LoginPage'
LoginPage.visit()
LoginPage.fillEmail('user@test.com')
설명
페이지 객체 패턴으로 UI 변경 시 한 곳만 수정하면 되어 유지보수가 쉬워집니다.
11. 실패_시_스크린샷과_비디오_활용
개요
Cypress는 실패한 테스트의 스크린샷과 비디오를 자동으로 캡처하여 디버깅을 쉽게 만듭니다.
코드 예제
// cypress.config.js
module.exports = {
video: true,
screenshotOnRunFailure: true,
videosFolder: 'cypress/videos',
screenshotsFolder: 'cypress/screenshots'
}
// 수동 스크린샷
cy.screenshot('my-screenshot')
설명
실패한 테스트의 스크린샷과 비디오가 자동 저장되어 CI/CD에서 문제를 빠르게 파악할 수 있습니다.
12. 테스트_격리로_독립성_보장
개요
각 테스트가 독립적으로 실행되도록 쿠키, 로컬 스토리지, 세션을 정리합니다.
코드 예제
beforeEach(() => {
cy.clearCookies()
cy.clearLocalStorage()
cy.window().then((win) => {
win.sessionStorage.clear()
})
})
// 특정 쿠키만 유지
cy.clearCookies({ except: ['session_id'] })
설명
테스트 간 상태를 격리하여 이전 테스트의 영향을 받지 않고 독립적으로 실행되도록 합니다.
마치며
이번 글에서는 Cypress 실무 활용 팁에 대해 알아보았습니다. 총 12가지 개념을 다루었으며, 각각의 사용법과 예제를 살펴보았습니다.
관련 태그
#Cypress #E2E Testing #Custom Commands #API Mocking #Test Automation