프론트엔드 테스트

LeeKyungwon·2024년 7월 22일
0

프론트엔드 

목록 보기
56/56
post-custom-banner

테스트란 프로그램을 실행하여 오류와 결함이 있는지 확인하고 요구사항에 맞게 동작하는지 검증하는 절차이다.

프론트엔드 개발자가 디자인을 보고 마크업과 스타일링 코드를 작성한 뒤, 브라우저에서 렌더링된 결과를 확인하는 일련의 과정이 테스트이다. 또한, 기존의 기능을 변경하거나 새로운 기능을 추가한 뒤 원하는 동작을 하는지 브라우저를 통해 확인하는 것도 테스트에 해당한다.

테스트 유형

정적 테스트

코드를 실행하지 않고 테스트하는 것을 말한다.
타입 에러나 잘못된 참조로 인한 에러와 같이 개발자의 실수로 발생할 수 있는 에러를 미리 방지할 수 있다.
ESLint를 활용해서 리액트의 dependency list에 있어야 할 값이 빠진 경우를 파악하기도 하고, 타입스크립트로 함수의 파라미터 타입을 검사하는 것이 정적 테스트에 해당한다.

단위 테스트

하나의 모듈을 하나의 독립된 환경에서 테스트하는 것을 말한다.
프론트엔드에서는 유틸성 함수, 커스텀 훅, 외부 의존성이 없는 하나의 컴포넌트 등을 테스트한다.

jest를 활용해 단위 테스트를 만들어 함수의 동작을 검증할 수 있다.

// validateCoupon.test.ts

import { validateCoupon } from "./validateCoupon";

describe("쿠폰 문자열이 형식에 맞는지 검증", () => {
  test("빈 문자열인 경우 유효하지 않음을 검증할 수 있다.", () => {
    // given
    const couponString = "";
    // when
    const result = validateCoupon(couponString);
    // then
    expect(result).toEqual(false);
  });

  test("특수 문자가 포함된 경우 유효하지 않음을 검증할 수 있다.", () => {
    // given
    const couponString = "cb2#21";
    // when
    const result = validateCoupon(couponString);
    // then
    expect(result).toEqual(false);
  });

  test("한글이 포함된 경우 유효하지 않음을 검증할 수 있다.", () => {
    // given
    const couponString = "한글포함123abc";
    // when
    const result = validateCoupon(couponString);
    // then
    expect(result).toEqual(false);
  });

  test("숫자 또는 알파벳인 경우 유효함을 검증할 수 있다.", () => {
    // given
    const couponString = "123abc";
    // when
    const result = validateCoupon(couponString);
    // then
    expect(result).toEqual(true);
  });
});

test 는 각각의 테스트 케이스를 나누기 위해 사용한다. given, when, then 주석은 테스트 코드에서 각각이 어떤 역할을 하는지 설명하기 위해 사용함
expect 함수는 검증에 필요한 다양한 메소드를 지원한다.
yarn jest 로 실행할 수 있음

통합 테스트

두 개 이상의 모듈을 연결된 상태로 테스트하는 것을 의미한다.
이를 통해 모듈 간의 연결에서 발생하는 에러를 검증하고, 단위 테스트보다 비교적 넓은 범위를 테스트한다.
UI와 유저 이벤트 사이에 상호작용이 의도한 대로 일어나는지 또는 상태의 변경에 따라 의도한 UI 변경이 일어나는지 등을 테스트한다.

E2E 테스트

End To End 테스트의 약자로 애플리케이션의 흐름을 처음부터 끝까지 테스트하는 것을 의미한다. 실제 사용자의 입장 및 환경에서 테스트하는 것이다.
E2E 테스트는 실제 브라우저를 실행해서 테스트하는 것을 말하며 실제 상황에서 발생할 수 있는 에러를 검출할 수 있다는 장점, Web API를 활용할 수 있고 테스트 코드가 내부 구조에 큰 영향을 받지 않기 때문에 코드의 변경에도 비교적 잘 동작한다는 장점이 있다.
하지만 브라우저 환경에서 실행하기 때문에 앞서 살펴본 테스트보다 실행 속도가 느리고 테스트의 실행 환경에서 발생하는 브라우저의 버전, 네트워크 속도 문제 등으로 테스트가 실패할 수 있다는 단점도 있다.

E2E 테스트로 사용하는 도구는 Cypress, Playwright, Selenium 등 다양하다.

Cypress로 테스트

Cypress
npm install cypress --save-dev 명령어로 설치하고,
npx cypress open 실행하면 Cypress GUI를 볼 수 있고, E2E를 위한 테스트 코드를 작성할 수 있다.
참고

테스트를 잘하기 위한 팁

테스트 코드는 제품 코드와 다르게 단순하고, 짧다.
테스트 코드는 빠르게 의도를 파악할 수 있도록 단순하고 간결하게 작성하는 것이 좋다.

테스트 코드 작성 구조

GWT(given, when, then) 또는 이와 유사한 AAA(arrange, act, assert)와 같이 섹션을 잘 구분해서 테스트를 작성해야 한다.

  • given (arrange): 테스트를 위해 변수 설정, 입력값 정의 등 준비하는 과정
  • when (act): 테스트할 코드를 실행하는 과정
  • then (assert): 테스트할 결과를 검증하는 과정

GWT는 비즈니스 요구사항에 초점을 맞춰 사용자 행동 관점에서, AAA는 테스트하는 개발자 관점에서 구분하기 위해 다른 단어를 사용했다는 차이이고 각 영역에 들어올 내용에는 차이가 없다.

테스트 싸이클을 반복하면서 작성


단위 테스트를 작성할 때는 처음에 실패하는 테스트 케이스를 먼저 작성하고, 실패하는 것을 확인한 후에 테스트 코드를 성공하기 위한 실제 제품 코드를 작성한다. 다음으로 테스트 코드를 성공하기 위해 작성했던 제품 코드에서 중복을 제거하고, 필요한 추상화를 하며 리팩토링을 수행한다. 그리고 이 과정을 반복한다.

기능과 화면을 분리하여 검증

컴포넌트 로직을 테스트할 때, 화면의 세부사항들을 제외한 데이터에 집중해서 검증하는 것이 좋다. 이를 위해 데이터를 마크업에서 추출해서 순수한 데이터를 검증해야 한다. 그렇지 않으면 애니메이션과 같이 화면에 종속적인 내용을 검증하기 위해 대기하는 시간이 길어질 수 있다.

// Coupon.test.tsx

...

test(`사용 가능한 쿠폰 번호를 입력하고 쿠폰번호인증 버튼을 클릭할 경우, ${MESSAGE.usable}를 확인할 수 있습니다.`, async () => {
  // given
  fireEvent.change(input, { target: { value: "successCouponTest" } });
  // when
  fireEvent.click(button);
  const text = await waitFor(() => screen.getByText(MESSAGE.usable));
  // then
  expect(text.textContent).toEqual(MESSAGE.usable);
});

...

위와 같이 Text 컴포넌트 안의 데이터에 해당하는 textContent를 검증하는 것이 바람직하고, 아래와 같이 화면의 요소인 span 태그를 포함하여 검증하는 것은 좋지 않다.

// Coupon.test.tsx

...

test(`사용 가능한 쿠폰 번호를 입력하고 쿠폰번호인증 버튼을 클릭할 경우, ${MESSAGE.usable}를 확인할 수 있습니다.`, async () => {
  // given
  fireEvent.change(input, { target: { value: "successCouponTest" } });
  // when
  fireEvent.click(button);
  const text = await waitFor(() => screen.getByText(MESSAGE.usable));
  // then
  expect(text).toEqual(
    <span className="text" data-cy="text">
      ${MESSAGE.usable}
    </span>
  );
});

...

테스트 자동화

앞서 살펴본 테스트 항목들을 매번 사람이 실행하고 확인하려면 테스트 실행을 누락할 수 있고, 모든 범위의 테스트를 매번 실행하는 데는 적지 않은 시간이 걸릴 수 있다. 따라서 실무에서는 배포 프로세스의 일부로 테스트를 거치도록 하는 방법을 사용한다.
GitHub Actions를 활용해 lint와 같은 정적 테스트부터 Cypress를 활용하는 E2E테스트까지 테스트 프로세스를 자동화할 수 있는데, 구체적으로 해당 내용을 참고해서 실행해 볼 수 있습니다. 이렇게 테스트를 배포 프로세스의 일부로 자동화하면 새롭게 배포된 코드에 대해서도 최소한의 검증과정을 거쳐 보다 안정적인 서비스를 제공할 수 있다.

이 밖에도 테스트를 위한 다양한 팁들이 존재하는데 javascript-testing-best-practices, 프런트엔드 단위 테스트 모범 사례 등을 참고하면 테스트 코드를 작성하는데 도움이 될것이다.

post-custom-banner

0개의 댓글