728x90
전통적인 테스트 도구들은 브라우저 외부에서 원격으로 명령을 전송하는 방식이었다면, Cypress는 브라우저 내부에서 직접 실행됩니다. Cypress를 실행하면 실제 브라우저 창이 열리고, 테스트가 실행되는 모든 과정을 실시간으로 시각적으로 확인할 수 있습니다.
타임 트래블 기능도 지원합니다. 테스트의 각 단계를 클릭하면 그 시점의 상태로 되돌아가서 DOM의 상태, 네트워크 요청, 콘솔 로그 등을 자세히 살펴볼 수 있습니다.
설치 과정
1. 프로젝트 루트 디렉토리에서 다음 명령어를 실행합니다.
npm install cypress --save-dev
2. package.json에 스크립트를 추가합니다.
{
"scripts": {
"cy:open": "cypress open",
"cy:run": "cypress run"
}
}
3. 초기 실행과 설정
npm run cy:open
이 명령어를 처음 실행하면 Cypress가 프로젝트를 분석하고 필요한 설정 파일들을 자동으로 생성합니다. cypress 폴더가 생성되고, 그 안에 여러 하위 폴더들이 만들어집니다.
Cypress 핵심 명령어
1) 페이지 탐색 명령어
- cy.visit(url) : 페이지에 접속합니다. 모든 E2E 테스트의 시작점입니다.
cy.visit('/') // 기본 URL로 이동
cy.visit('/login') // 로그인 경로로 이동
cy.visit('<https://example.com>') // 절대 URL로 이동
- cy.go(direction) : 브라우저 히스토리를 조작합니다.
cy.go('back') // 뒤로 가기
cy.go('forward') // 앞으로 가기
cy.go(-1) // 한 페이지 뒤로
2) 요소 선택 명령어
- cy.get(selector) : CSS 선택자로 DOM 요소를 찾습니다.
cy.get('button') // 모든 button 태그
cy.get('.btn-primary') // class가 btn-primary인 요소
cy.get('#submit-btn') // id가 submit-btn인 요소
cy.get('input[type="text"]') // text 타입의 Input
cy.get('[data-cy="tagName"]') // data-cy 속성 -> 가장 권장되는 방식
cy.get('ul li:first-child') // 첫 번째 li 요소
- cy.contains(content) : 텍스트 내용으로 요소를 찾습니다.
cy.contains('로그인') // "로그인" 텍스트를 포함한 요소
cy.contains('button', '확인') // button 태그 중 "확인" 텍스트가 존재하는 것
cy.contains(/정규표현식/) // 정규표현식으로 매칭
- cy.find(selector) : 이미 선택된 요소의 하위 요소를 찾습니다.
- cy.get() 은 전체 문서에서 찾고, cy.find() 는 이미 선택된 요소 내에서만 찾습니다.
cy.get('.todo-list').find('li') // todoList 클래스 안의 li 모두
cy.get('[data-cy="form"]').find('input') // 폼 안의 input 모두
3) 사용자 상호작용 명령어
- cy.type(text) : 텍스트를 입력합니다.
- 특수키 :
- {enter}, {tab}, {esc}, {backspace}
- {selectall}, {del}, {home}, {end}
- {ctrl + a}, {shift + tab}
cy.get('input').type('안녕하세요')
cy.get('input').type('텍스트{enter}') // Enter키도 함께 입력
cy.get('input').type('{selectall}새텍스트') // 전체선택(Ctrl+A) 후 입력 -> 기존 텍스트 삭제
cy.get('input').type('test@email.com')
- cy.click() : 요소를 클릭합니다.
cy.get('button').click()
cy.contains('제출').click()
cy.get('input[type="checkbox"]').click() // 체크박스 토글
cy.get('.item').click( { multiple : true }) // 여러 요소 클릭
cy.get('button').click({ force : true }) // 숨겨진 요소도 강제 클릭
cy.get('button').click({ position : 'top' }) // 특정 위치 클릭
- cy.clear() : 입력 필드의 내용을 지웁니다.
cy.get('input').clear()
cy.get('input').clear().type('새로운 내용') // 지우고 새로 입력
- cy.select(value) : select 박스에서 옵션을 선택합니다.
cy.get('select').select('option1') // value로 선택
cy.get('select').select('한국어') // 텍스트로 선택
cy.get('select').select(['otp1', 'otp2']) // 다중 선택
- cy.check() / cy.uncheck() : 체크박스나 라디오 버튼을 조작합니다.
cy.get('[type="checkbox"]').check() // 체크
cy.get('[type="checkbox"]').uncheck() // 체크 해제
cy.get('[type="radio"]').check('value') // 특정 라디오 선택
4) 검증(Assertion) 명령어
- cy.should(assertion : 요소의 상태를 검증합니다. 테스트의 핵심입니다.
// 존재와 가시성
cy.get('button').should('exist') // 요소가 DOM에 존재
cy.get('button').should('be.visible') // 요소가 화면에 보임
cy.get('button').should('not.exist') // 요소가 존재하지 않음
cy.get('button').should('be.hidden') // 요소가 숨겨져 있음
// 텍스트와 내용
cy.get('h1').should('contain.text', 'Todo List') // 텍스트 포함
cy.get('h1').should('have.text', 'Todo List') // 정확한 텍스트
cy.get('input').should('have.value', '입력된 값') // input의 value
cy.get('div').should('be.empty')
// 상태와 속성
cy.get('button').should('be.disabled') // 비활성화됨
cy.get('button').should('be.enabled') // 활성화됨
cy.get('input').should('be.focused') // 포커스됨
cy.get('input').should('have.attr', 'placeholder', '입력하세요')
cy.get('div').should('have.class', 'active') // CSS 클래스 확인
// 개수와 길이
cy.get('li').should('have.length', 3) // 요소 개수
cy.get('li').should('have.length.greaterThan', 0) // 0개보다 많음
cy.get('ul').should('contain', 'list item') // 내용 포함
- cy.then(callback) : 요소를 가져와서 추가 작업을 수행합니다.
cy.get('li').then($elements => {
expect($elements).to.have.length(3)
// jQuery 객체로 추가 조작 가능
})
// 텍스트 내용을 변수에 저장
cy.get('.count').then($el => {
const count = $el.text()
cy.wrap(count).should('equal', '5')
})
5) 대기 명령어
- cy.wait(time) : 지정된 시간만큼 대기합니다. (일반적으로 권장하지 않음)
cy.wait(1000) // 1초 대기 - 가능하면 피하세요!
// 더 나은 방법: 요소가 나타날 때까지 자동으로 대기하는 should() 사용
- cy.intercept() : 네트워크 요청을 가로채고 모킹합니다.
// API 응답 모킹
cy.intercept('GET', '/api/todos', { fixture: 'todos.json' })
// 요청 완료까지 대기
cy.intercept('POST', '/api/todos').as('createTodo')
cy.get('button').click()
cy.wait('@createTodo') // 이때는 wait() 사용이 적절
6) 페이지 상태 조작
- cy.url() : 현재 URL을 검증합니다.
cy.url().should('include', '/dashboard')
cy.url().should('eq', '<http://localhost:3000/login>') // 가져온 URL과 일치하는지 검증
- cy.title() : 페이지 제목을 검증합니다.
cy.title().should('contain', 'Todo List')
- cy.viewport(width, height) : 화면 크기를 변경합니다.
cy.viewport(375, 667) // iPhone 크기
cy.viewport(1280, 720) // 데스크톱 크기
cy.viewport('iphone-6') // 프리셋 사용
7) 고급 명령어
- cy.wrap(object) : Javascript 객체를 Cypress 체인에 포함시킵니다.
const obj = { name: 'test' }
cy.wrap(obj).should('have.property', 'name', 'test')
- cy.window() : 브라우저 window 객체에 접근합니다.
cy.window().its('localStorage').invoke('getItem', 'token')
cy.window().its('location.pathname').should('eq', '/dashboard')
- cy.document() : document 객체에 접근합니다.
cy.document().its('readyState').should('eq', 'complete')
8) 자주 사용하는 패턴들
- 조건부 테스트
cy.get('body').then($body => {
if ($body.find('.error-message').lenght > 0){
// 에러 메세지가 있으면 처리
cy.get('.error-message').should('be.visible')
} else{
//없으면 다른 처리
cy.get('.success-message').should('be.visible')
}
})
- 체이닝 활용
cy.get('input')
.clear() // 기존 내용 지우기
.type('새로운 할 일') // 텍스트 입력
.should('have.value', '새로운 할 일') // 검증
.parent() // 부모 요소로 이동
.find('button') // 버튼 찾기
.click() // 클릭
- 재사용 가능한 패턴
// 자주 사용하는 동작을 함수로 추출
const addTodo = (text) => {
cy.get('input[placeholder*="할 일"]').clear().type(text)
cy.contains('추가').click()
}
// 사용
addTodo('Cypress 공부하기')
9) 선택자 우선순위
- data-cy 속성 (가장 안정적)
- data-test 속성
- id 속성
- CSS 클래스 (변경 가능성 있음)
- 태그명 (가장 불안정)
728x90
'프론트엔드' 카테고리의 다른 글
프론트엔드 테스트 기본기와 Cypress (0) | 2025.05.23 |
---|---|
CSS의 레이아웃 (1) | 2025.01.22 |
CSS 기본 개념 (1) | 2025.01.19 |
HTML 기본 태그 정리 및 주요 기능 (2) | 2025.01.17 |