Cypress 사용기

김장훈·2020년 2월 9일
1

E2E 테스트를 적용한 이유

  • 테스트에는 다양한 방법이 있고 백엔드 개발을 할때에는 유닛테스트와 피처테스트를 사용하였다.
  • 백엔드의 경우 api와 1대1로 대응 되게 테스트를 작성하면 끝이었기에 테스트에 대한 고민은 크게 안하였다.(물론 차후에 BDD를 접하면서 생각이 달라짐)
  • 하지만 프론트의 경우 단순히 유닛테스팅을 하기에는 너무 막막한 상황이 많았다(무엇을 테스트 해야할지에 대한 고민이 많았음)
  • 예를 들어 어떤 버튼을 클릭했을때 액션이 일어나야한다고 할 경우, vuex등을 사용하여 저장소와 같이 테스팅 되야하는 경우 등이 그러했다.
  • 그렇기에 프론트의 경우엔 유닛 테스팅이 아닌 E2E 테스팅을 먼저 적용시키는게 낫다 라고 결론 지었다.
  • 현재 vue의 경우 곧 3.0 버젼이 나오고 여기에 타입스크립트가 적용되다 보니 유닛테스팅 역시 아예 새롭게 적용해야할 이슈가 있다고 판단되어 유닛테스팅을 도입하지 않았음.
  • 특히 지금 Vue + ts 의 경우 vuex를 이용하는 유닛 테스팅 관련 레퍼런스도 매우 적어서 뭘 못하는 상황...

현재 todo 프로젝트에 적용해서 진행중

  • 현재 todo 프로젝트에 적용중인 모습

cypress 사용기

  • cypress를 실제 서버와 상호작용 하는 방법이 있고 프론트만 별개로하여 단독으로 작동시키는 방법이 존재함
  • 단독으로 작동시키는게 베스트이긴 하지만 현실적인 문제가 있어서 현재는 실제 서버와 붙여서 사용중

단독으로 사용한다고 할 경우

  • cypress를 설치하게 되면 feature 폴더가 생기는데 여기에 api 명세서와 예시데이터를 올려서 사용하면 된다.

실제 서버와 붙여서 사용 할 경우

  • 실 서버와 붙여서 사용할때 제일 문제가 되는 부분은 바로 인증부분이다. 보통 토큰을 통해서 인증을 하다보니 api를 날릴때 토큰을 붙여줘야하는 이슈가 있다.
  • 이럴 경우 stub을 사용하여 request 또는 response를 내가 원하는 방식대로 바꿔줘야한다.
cy.server()
cy.route({
	url: '/some/api/what/you/need',
	method: 'POST',
	onRequest: xhr => {
	xhr.setRequestHeader("Authorization", `Bearer ${token}`);
	},
})
  • 이런식으로 할 경우에 특정 api 또는 *를 통해서 request를 조작할 수 있다(위의 경우엔 header에 토큰을 넣는다)
    (물론 이 방법이 베스트는 아님)
  • 하나 또 문제가 된 부분은 로그인하여 가져온 token을 바깥으로 빼지 못하여 매 api를 할때마다 로그인 -> 받아온 토큰 사용
    (promise로 가져온 데이터를 바깥으로 못빼는 이슈인데, 로컬스토리지나 cypress-storage를 사용해도 해결이 안됨. 차후 더 확인해봐야함)

component를 쪼개서 사용할 경우 손이 많이 간다

  • cypress에서 권고하는 사항은 커스텀 마크업을 만들어서 이를 통해 제어하는 방법임
<Body :headers="getHeaders" :todoList="getTodoList" 
:method="onRowClick" data-cy="todo__table">
</Body>
  • 위에처럼 data-cy를 통해 cypress를 제어하는데 문제는 컴포넌트를 모두 쪼개서 사용할 경우(버튼 컴포넌트, input compoent 등) 이를 직접 접근할 수 없기에 data-cy로 접근 후에 필드를 더 찾아줘야했다.
cy.get('.dropdown-menu').click()
  • 위의 경우 바로 dropdown 메뉴로 명령을 내리는 구조이지만 컴포넌트로 쪼갤 경우엔 컴포넌트 안에서 찾아줘야한다.
<UiInput :someValue="dataDto.title" 
:method="getContents" 
data-cy="todo__dialog__input--title"/>
// UiInput 컴포넌트
<template>
  <v-text-field :rules="parentRules" v-model="value" :label="label" @change="onChange" />
</template>
  • 위 예시를 보면 UiInput 컴포넌트를 재사용하는 구조인데 단순히 tododialoginput--title로 접근할 경우 div로 접근하기 때문에 그 아래에서 다시한번 찾아줘야한다. 이럴 경우
cy.get("[data-cy=todo__dialog__input--title]").find('input').focus().blur()
  • 위에처럼 get으로 접근 후 find를 사용하는 방식으로 해봤는데, 이 방법도 맞는지 의문이긴 하다.

컴포넌트에서 value를 뽑는 방법

  • input 컴포넌트에서 data 비교를 위해서 input에 입력된 text를 가져오는 방법
const title = "Flask Todo project"
cy.get("[data-cy=todo__dialog__input--title]").find('input').invoke('val').should("contain", title);

결론

  • 테스트를 작성하는 것은 느리고 귀찮고 힘들다. 하지만 테스트가 완성만 되면 너무 큰 도움이 된다.
  • 컴포넌트를 모두 쪼개서 사용할 경우 기존 작성법 보다 손이 더 많이 간다.
profile
읽기 좋은 code란 무엇인가 고민하는 백엔드 개발자 입니다.

2개의 댓글

comment-user-thumbnail
2020년 11월 10일

따로 cypress repo를 만드셨나요? 아니면 테스트랑 기존 소스랑 통합인가요?

1개의 답글