Skip to content

Cypress 와 Jest 를 활용한 테스트 🧪

오지훈 edited this page Dec 20, 2020 · 8 revisions

프로젝트의 핵심적인 기능 개발을 마친 후 그다음으로 진행한 작업은 바로 테스트 코드 작성! 그 과정을 소개해드립니다 😊

바쁘신 분들을 위한 3줄 요약!

  • 테스트를 통한 주기적인 버그 식별 및 기능 동작 확인 시간 감소
  • 사용자 시나리오에 따른 E2E 테스트를 위해서 Cypress 사용
  • Jest를 이용한 통합 테스트 및 seed 사용

테스트 코드를 작성한 이유는 뭔가요?

추가적인 기능을 구현하거나 특정 부분을 리팩토링하면 서비스의 여러 부분이 영향을 받게 됩니다. 이때 발생하는 버그를 더 빠르게 찾고 이전에 구현한 기능들이 제대로 잘 동작하는지 확인하는 데 걸리는 시간을 줄이고자 테스트 코드를 작성했습니다.

무엇을 이용해서 테스트 하나요?

백엔드의 통합 테스트를 위해서 Jest를 사용하고 E2E 테스트를 위해서는 Cypress를 사용했습니다. 또한 저희는 Jest 를 단위 테스트가 아닌 통합테스트 에 사용했습니다. 저희 프로젝트에서 API 요청은 복잡한 데이터를 처리하는 비즈니스 로직 대신 단순히 db에서 값을 조회하고 수정해 클라이언트에게 전달하는 코드가 많았으므로 단위 테스트 대신 API가 잘 동작하는지 확인하는 통합 테스트를 진행했습니다.

백엔드 통합 테스트를 위한 준비 과정은 무엇이 있나요?

저희는 테스트를 위한 데이터베이스를 따로 두었습니다. 통합 테스트로 진행되다 보니 API 호출 시 실제 데이터에 변화가 생기게 되었습니다. 테스트로 인해서 배포된 서버가 직접적인 영향을 받으면 안 된다고 판단해서 별도의 데이터베이스를 두었습니다.

또한, seed를 직접 구현해서 데이터베이스를 매번 초기화시켰습니다. 테스트는 프로젝트에 기능이 추가되거나 수정될 때마다 돌리게 됩니다. 하지만 회원가입과 같이 고유한 정보로 데이터를 생성해야 하는 API들은 에러를 반환하게 되고 이를 해결하기 위해 데이터를 초기화했습니다.

Jest로 테스트 코드는 어떻게 작성하나요?

API 단위로 테스트 코드를 작성하고 올바른 결괏값이 반환되었는지, 변경 사항이 잘 적용되었는지를 확인했습니다. 예시로 설명하겠습니다.

실시간으로 뜨는 택시 호출 요청을 드라이버가 수락했을 때 실행되는 운행 시작 API를 아래와 같이 테스트했습니다. 오더의 아이디와 함께 해당 API를 실행합니다. 로직이 성공적으로 실행되었을 경우 success를 결괏값으로 반환하기 때문에 해당 값이 들어왔는지 확인합니다. 추가로 해당 오더를 직접 불러와서 상태가 올바르게 바뀌었는지 확인했습니다.

자세한 코드가 궁금하시다면 여기❗️를 클릭해주세요 😊

test('approvalOrder API 테스트', async () => {
    const {
      data: { approvalOrder },
    } = await mutate({
      mutation: APPROVAL_ORDER,
      variables: { orderId: waitingOrderId },
    });

    const {
      data: { getOrderById },
    } = await query({
      query: GET_ORDER_BY_ID,
      variables: { orderId: waitingOrderId },
    });

    expect(approvalOrder.result).toBe('success');

    expect(getOrderById.result).toBe('success');

    expect(getOrderById.order.status).toBe('approval');
  });

Cypress로 테스트 코드는 어떻게 작성하나요?

Cypress는 서비스가 사용자의 입장에서 사용 시나리오대로 잘 동작하는지를 테스트합니다. 따라서 사용자에 의해서 특정 이벤트가 발생했을 때 기대하는 동작이 나타나고 이에 대한 시각적 변화가 화면에 반영되는지 확인합니다. 추가로 올바른 데이터를 잘 불러왔는지도 확인합니다.

자세한 코드가 궁금하시다면 여기❗️를 클릭해주세요 😊

it('로그인 정보가 일치하지 않을 경우 toast 메세지가 나타난다.', () => {
  cy.get("input[type='text'").type(Cypress.env('INVALID_USER_EMAIL'));
  cy.get("input[type='password'").type(Cypress.env('INVALID_USER_PASSWORD'));
  cy.get("[data-testID='login-button']").click();

  cy.get('.am-toast').should('exist');
});

Cypress를 실행시키면 작성한 코드가 어떻게 테스트 되는지를 화면을 통해서 시각적으로 볼 수 있습니다. 또한, 중간에 실패한 테스트가 있을 때 에러를 발생한 해당 스텝(step)으로 되돌아가서 어떤 부분이 틀렸는지를 매우 편하게 찾을 수 있었습니다.

또한 저희는 기존의 스타일을 Ant Design으로 마이그레션하는 과정이 있었습니다. 저희의 회원가입 페이지와 로그인 페이지는 대부분 Styled Component로 작성되어 있었기 때문에 Ant Design으로 마이그레션하는 과정에서 올바르게 동작하는지에 대한 걱정이 있었습니다. 하지만 저희는 테스트 코드를 작성했기 때문에 코드 수정과 테스트를 병행하면서 작업을 진행했고 빠르게 Ant Design으로 마이그레션하는데 이점을 얻을 수 있었습니다.

Cypress

결론

리팩토링 일정이 빠지게 되면서 테스트의 장점을 경험하지 못한 것이 살짝 아쉬웠습니다. 하지만 테스트 코드를 작성해서 저희 서비스가 잘 동작하고 있다는 것을 단번에 확인할 수 있어서 좋았습니다.

테스트 코드 작성을 모두 처음 해봐서 이번 프로젝트에서는 맛보기로 진행되었습니다. 이후에는 기능 개발과 테스트 코드 작성을 서로 병행하면 동작을 확인하는 시간이 줄어서 개발 속도를 높일 수 있다고 기대합니다.

Clone this wiki locally