본문 바로가기

Study

Cypress로 테스트 코드 작성하기

※ 이 글은 2019년도에 작성된 글을 기반으로 작성되었습니다. (원본참조)

 

내가 테스트 코드를 알게된 건 신입때였다. 그 당시 다니던 회사에는 개발을 완료하면 QA팀에서 검수를 하여 피드백을 받는 식으로 작업을 했었는데 QA팀에 일이 몰려 검수가 늦어지고 검수 중에 자잘한 피드백이 많이 발생해 마감일정이 미루어지는 일이 비일비재 했었다. 그래서 QA 검수 기간을 줄여보고자 여러 방안을 모색하던 중 테스트 코드란 것을 접하게 되었다.

처음에는 jasmine과 karma를 사용하여 테스트 코드를 작성했는데 한 페이지에 대한 테스트 코드를 작성하려면 그 페이지에서 사용하는 모듈들을 전부 import해야하는 번거로움이 있었다.

(지금 생각해보면 내가 하고 싶었던 것은 E2E 테스트이고 jasmine과 karma는 단위 테스트에 특화되어있기에 힘들었던거였다..)

 

고민을 하던 중 참여한 NHN Forward에서 Cypress를 알게 되었다. 고민해오던 것에 맞는 완벽한 솔루션이었기에 그 다음날 바로 Cypress로 테스트 코드를 작성해보았고 그 결과물을 팀원들과 공유하여 팀내 테스트 도구로 정착하게되었다.

아래 글은 그 당시 팀원들에게 공유를 위해 작성한 Cypress 가이드 이다.

 

 

GitHub - yoonovo/cypress-test-code: Cypress 설명

Cypress 설명. Contribute to yoonovo/cypress-test-code development by creating an account on GitHub.

github.com

 


 

Cypress 란

  • Cypress는 mocha/chai를 기반으로 하는 프론트엔드 테스트 도구
  • 연동된 모든 모듈을 하나하나 import 해야하는 jasmine/karma와는 달리 import 할 필요없이 사용가능하고 설치하거나 테스트 하는 방법도 간단하다.
설치하기

Cypress는 npm 명령어로 간단하게 설치 할 수 있다.

npm i cypress

 

설정하기

설치가 끝났다면 바로 npx cypress open 으로 시작 할 수 있지만 직접 시작 명령어를 설정하는 것을 추천한다.

  • cypress open : 시작 명령어 (새 창으로 테스트 실행)
  • cypress run : 터미널에서 간의 테스트를 실행
    // package.json
    "scripts": {
      "cy:open": "cypress open",
      "cy:run": "cypress run"
    }
시작하기

Cypress는 외부에서 가상으로 검증 하는 것이 아니라 로컬 서버에서 직접 테스트하는 테스트 도구이기 때문에 서버를 먼저 시작한 후 Cypress를 실행해야한다.

npm run start

로컬 서버를 실행 시켰다면 Cypress를 시작 해보자

npm run cy:open

처음 Cypress를 실행하면 "cypress"라는 최상위 폴더와 그 안의 하위 폴더들이 생성된다.
또 "cypress/integration"안의 "examples" 폴더가 생성되는데 초보자를 위한 테스트 코드 예제파일들이다.

Cypress 실행 시 생성되는 폴더들

 

테스트 러너

테스트 러너를 자세히 살펴보자.
오른쪽 상단에 보이는 Run all specs 버튼으로 모든 테스트 코드 파일을 검증 할 수도 있고 하나의 파일을 클릭하면 단일 파일의 테스트 코드만 검증 할 수도 있다. 테스트 러너 목록에 보이는 파일은 "cypress/integration"안의 저장된 파일들로 "cypress/integration" 폴더 안에 signIn.spec.ts라는 테스트 코드 파일을 생성해보면 테스트 러너에 방금 생성한 signIn.spec.ts 파일이 보일 것이다.

 

@testing-library/cypress 설정하기

최초로 Cypress를 실행하여 폴더를 생성 했다면 Cypress 라이브러리를 하나 설치한 뒤 설정도 해준다.

이 라이브러리는 테스트 코드 전용 ID를 생성하여 HTML 내에 id, class name 등이 변경되어도 DOM을 가져올 수 있도록 해준다.

npm i @testing-library/cypress
// tsconfig.json
{
  "compilerOptions": {
    "types": ["cypress", "@types/testing-library__cypress"]
  }
}
// cypress/support/commands.js
import '@testing-library/cypress/add-commands'

 

테스트 코드 작성

테스트 러너에서 signIn.spec.ts 파일을 클릭하여 실행시켜보면 아래 이미지와 같은 화면이 나올 것이다.

 

테스트 코드를 살펴보자.

describe('비정상적으로 로그인 했을 경우', () => {
  beforeEach(() => {
    cy.visit('http://localhost:4200')
  })

  it('아이디와 비밀번호 둘 다 미입력 상태에서 로그인 버튼을 눌렀을 때 "아이디 또는 비밀번호를 확인해 주세요."라는 알럿창이 떠야한다.', () => {
    cy.get('.id-input').should('be.empty').should('have.css', 'border', '1px solid rgb(255, 0, 0)')
    cy.get('.pw-input').should('be.empty').should('have.css', 'border', '1px solid rgb(255, 0, 0)')

    cy.contains('로그인').click()

    const stub = cy.stub()
    cy.on('window:alert', stub)
    cy.contains('로그인').click().should(() => {
      expect(stub.getCall(0)).to.be.calledWith('아이디 또는 비밀번호를 확인해 주세요.')      
    })
  })

  it('비밀번호만 미입력 상태에서 로그인 버튼을 눌렀을 때 "아이디 또는 비밀번호를 확인해 주세요."라는 알럿창이 떠야한다.', () => {
    cy.get('.id-input').type('yoonovo').should('have.css', 'border', '1px solid rgb(0, 0, 0)')
    cy.get('.pw-input').should('be.empty').should('have.css', 'border', '1px solid rgb(255, 0, 0)')

    cy.contains('로그인').click()
    const stub = cy.stub()
    cy.on('window:alert', stub)
    cy.contains('로그인').click().should(() => {
      expect(stub.getCall(0)).to.be.calledWith('아이디 또는 비밀번호를 확인해 주세요.')      
    })
  })
})

describe('정상적으로 로그인 했을 경우', () => {
  beforeEach(() => {
    cy.visit('http://localhost:4200')
  })
  it('아이디와 비밀번호를 입력한 뒤 로그인 버튼을 눌렀을때 "로그인 되었습니다."라는 알럿창이 떠야한다.', () => {
    cy.get('.id-input').type('yoonovo').should('have.css', 'border', '1px solid rgb(0, 0, 0)')
    cy.get('.pw-input').type('yoonovo').should('have.css', 'border', '1px solid rgb(0, 0, 0)')

    cy.contains('로그인').click()
    const stub = cy.stub()
    cy.on('window:alert', stub)
    cy.contains('로그인').click().should(() => {
      expect(stub.getCall(0)).to.be.calledWith('로그인 되었습니다.')      
    })
  })
})

mocha/chai 문법이기에 테스트 코드를 접해본 적이 있다면 익숙하게 느껴질 것이다.

위에서 사용된 Cypress 문법을 간단하게 알아보자.

  • cy.visit('url') : url로 이동
  • cy.get() : 테스트 할 DOM 객체를 가져온다. DOM 객체의 태그, 클래스, 아이디로 가져올 수 있다. (자세한 내용은 여기)
  • cy.contains('String') : 문자열과 일치하는 DOM을 가져온다.
  • cy.get().should() : 가져온 DOM 객체의 속성이나 입력된 값을 비교 할 수 있다. (자세한 내용은 여기)
  • cy.get().click() : 가져온 DOM 객체에 클릭 이벤트를 발생시킨다.

이외에 나머지는 Cypress 공식 홈페이지에 들어가면 가이드와 설명이 잘되있으니 테스트 코드를 작성할 때 참고 바란다.

 

Data-TestId

위에서는 cy.get()으로 태그, 클래스, 아이디로 DOM을 가져왔다.
하지만 만약 cy.get()으로 가져온 DOM의 태그나 클래스, 아이디가 수정된다면 위의 테스트 코드는 실패 할 것이다. 클래스 하나 수정할 때마다 테스트 코드를 수정하기엔 비용이 과도하다. 이럴 때를 위해 우리는 @testing-library/cypress라는 Cypress 라이브러리를 하나 설치 했다.

이 라이브러리는 data-testId라는 테스트 전용 아이디를 제공하여 이 아이디로 DOM을 가져올 수 있게 한다.

  • 사용법
    테스트를 하기위해 가져오고 싶은 DOM 객체 태그에 아래와 같이 추가한다.
    <p data-testId="hello">반갑습니다.</p>
    그리고는 테스트 코드를 작성할 때 cy.findByTestId()로 DOM을 가져오면 된다.
    cy.findByTestId("hello")
    이런 방식으로 테스트코드를 작성하면 DOM의 클래스나 태그가 바뀌어도 테스트는 실패하지 않는다.

 

'Study' 카테고리의 다른 글

useSyncExternalStore로 외부 상태 안전하게 구독하기  (0) 2025.07.19
Sorting 알고리즘  (0) 2025.07.12
브라우저 렌더링 (Browser rendering)  (0) 2024.07.01