프론트엔드에서 테스트

❗️ 테스트란?

프로그램을 실행하여 오류와 결함을 검출하고 애플리케이션이 요구사항에 맞게 동작하는지 검증하는 절차이다.

즉, 개발자의 의도대로 코드가 동작하는가를 확인해보는 작업이다.

 

테스트는 두가지 종류로 나누어지는데

  1. 자동화 테스트 - 코드를 작성하고 반복적으로 테스트
  2. 수동 테스트 -  브라우저에서 직접 사용해보는 테스트

실제 사용자가 사용한다고 생각하고 직접 테스트를 해볼 수도 있지만(수동 테스트)

코드가 수정될 때마다 다시 테스트를 해야 하고, 실수를 할 가능성이 있기 때문에 테스트 코드를 작성하여 자동화를 한다.


❗️ 프론트엔드에서 테스트

프론트엔드에서 테스트 코드를 통해 테스트 코드를 자동화는 사례는 많지 않다고 한다.

프론트엔드에는 UI가 존재하는데, UI가 자주 변경되기 때문이라고 추측할 수 있다.

 

최근에는 프론트엔드에서도 테스트를 진행하는 추세이다.

 

🤔 어떤 것을 테스트 해야할까?

사용자에 의해 발생하는 이벤트(click, scroll, focus)에 의한 기능을 테스트 하는 것은

대부분의 테스트 프레임워크로 할 수 있기 때문에 큰 어려움이 없다.

 

하지만 우리가 기대한 화면과 실제 화면이 같은 지를 비교하는 것은 어렵다.

컴퓨터로 확인하기 위해서는 화면을 픽셀 단위로 잘라서 확인해야 하는데, 시간이 많이 소요되고 테스트 결과가 정확하지 않을 수 있다.

 

픽셀을 비교 하는 것보다 기능을 비교하는 방식을 사용할 수 있다.

예를 들어 어떤 버튼을 눌렀을 때, 텍스트가 등장했는지의 여부를 확인한는 것이다.

물론 정확성은 떨어지지만, 시간이 많이 소요되지 않고 거짓음성이 없다는 장점이 있다.

픽셀 비교 방법을 시각적 테스트, 기능이 작동하는지 확인하는 방법을 기능적 테스트라고 한다.

 

기능 테스트는 말 그대로 기능이 의도대로 동작하는 지 확인하는 방법이다.

기능 테스트에는 정적 테스트, 단위 테스트, 통합테스트, E2E 테스트가 있다.


❗️ 기능 테스트 종류

- Static Test(정적 테스트)

  • 구문 오류와 타입 오류를 감지해서 런타임 에러 방지한다.

도구: TypeScript, Eslint 

 

- Unit Test(단위 테스트)

  • 가장 작은 단위의 기능을 테스트한다.
  • 컴포넌트, 커스텀 훅, 유틸 함수 등을 테스트한다.
  • input에 대해 올바른 output을 반환하는지 테스트한다.

도구: jest, mocha 등

 

- Integration Test(통합 테스트)

  • 여러 개의 모듈과 컴포넌트 등이 상호작용하여 잘 동작하는지 테스트한다.
  • 비지니스 로직과 연관된 테스트이다.
  • API 테스트를 위해 모킹을 하는 것이 좋다.

도구 : enzyme 

 

- E2E Test

  • 사용자가 어플리케이션에서 경험할 것으로 예상되는 행동을 코드로 작성해서 검증하는 테스트이다.
  • 다루는 범위가 크다.

도구 : cypress, puppeteer, playwrite


❗️ 시각적 테스트

- 스냅샷 테스트

  • 예상 결과를 미리 정확히 포착해두고 실제 결과와 비교하는 방법이다.
  • css 속성이 변경된 경우는 감지할 수 없다.

- 시각적 회귀 테스트

  • 매 커밋시마다, 테스트 대상의 화면과 저장된 화면이 같은지 비교하는 방법이다.
  • 차이가 있다면 사용자의 approve 혹은 코드 수정을 통해 테스트를 통과시킨다. 

- UI 테스트

  • 컴포넌트가 예상한 대로 화면에 그려지는 지 테스트한다.

도구 : storybook, bit


❗️ 그 외의 테스트

- 웹 접근성 테스트

  • 장애인, 고령자, 저시력자, 색각 이상자 등 다양한 사용자 그룹이 웹사이트를 접근하고 이용할 수 있는 지 테스트한다.

도구 : storybook accessibility addons, 스크린 리더, wave

 

- 크로스 브라우저 테스트

  • 다양한 브라우저에서 웹/앱이 동일하게 동작하는지 테스트한다.

🤔 왜 테스트를 해야 할까?

프론트엔드 코드는 자주 바뀌는데 굳이 테스트를 해야 하나?라는 의문이 들 수 있다.

테스트에는 아래와 같은 장점들이 존재한다.

 

1. 피드백 사이클이 빨리진다.

 

2. 생산성이 많이 올라간다.

 

3. 안정성이 높아진다.

 

4. 개발 실력이 좋아진다.

=> 유닛 테스트 코드를 짜다 보면 해당 메서드가 어떤 책임을 가지고 있는지를 직관적으로 볼 수 있다.

 

예를 들어 어떤 버튼을 눌렀을 때, 페이지를 이동하는 코드가 있다고 가정해보자.

로직을 짜고 버튼 위치로 이동해서 버튼을 누르고 페이지로 넘어간다.

테스트 코드를 잘 작성해두면 이런 손으로 하는 테스트 없이 원하는 동작을 바로 코드로 확인할 수 있다.

 

아래 영상 보는 것 추천! 

https://toss.tech/article/firesidechat_frontend_3

 

모닥불 | EP.3 프론트엔드 개발에서 테스트 자동화, 꼭 해야 할까?

테스트 코드는 왜 써야 할까요? 테스트 코드가 가져오는 베네핏들에는 어떤 것이 있을까요?

toss.tech

 


❗️ Jest 사용해보기

실제 내가 했던 프로젝트에 Jest를 설치해서 사용해보았다.

Jest, Mocha, Jasmine 중에 Jest를 선택한 이유는 세팅이 간단하기 때문이다.

 

Jest를 설치하고 바로 테스트를 해보았다.

핸드폰 번호를 받아서 중간의 -를 제거하는 함수를 테스트해보았다.

export const formatPhoneNum = (phoneNum) => {
  if (phoneNum === '') return '';
  return phoneNum.replace(/-/g, '');
};

 

다음과 같이 테스트 코드를 작성했다.

 

import { formatPhoneNum } from '@/utils/utils';

describe('formatPhoneNum', () => {
  test('formatPhoneNum remove hyphens', () => {
    expect(formatPhoneNum('010-1234-5678')).toBe('01012345678');
  });
});

'010-1234-5678'를 넣으면 '01012345678'가 반환해야 한다는 의미이다.

 

이렇게 작성하고 'npm run test'를 터미널에 입력하면 테스트가 완료된다.


❗️ MSW로 API Mocking

MSW란 서비스 워커를 사용해서 네트워크 호출을 가로채는 API 모킹 라이브러리이다.

백엔드 API가 없을 때, API가 있는 것처럼 프론트엔드 요청에 가짜 데이터를 응답해주는 방법이다.

 

MSW 구조는 아래 사진을 보면 쉽게 이해할 수 있다.

 

먼저 MSW를 설치해준다.

npm install msw@latest --save-dev
npx msw init public/

첫번째 줄은 MSW를 가장 최근 버전으로 설치하고, 개발환경에서만 사용하겠다는 것이다.

두번째 줄은 MSW의 서비스 워커 파일을 생성하여 저장하는 것이다.

 

// src/mocks/handler.js
import { http, HttpResponse } from 'msw';

export const handlers = [
  http.get('/test', () => {
    return HttpResponse.json({ message: 'test' });
  }),
];
// src/mocks/broswer.js
import { setupWorker } from 'msw/browser';
import { handlers } from './handler.js';

export const worker = setupWorker(...handlers);
// src/App.vue
onMounted(() => {
  worker
    .start()
    .then(() => {
      console.log('MSW가 성공적으로 시작되었습니다.');
      fetchData();
    })
    .catch((err) => {
      console.error('MSW 시작 중 오류:', err);
    });
}

 

위와 같이 설정하고 실행시키면 된다.

 

브라우저 네트워크 탭을 열어보면 요청이 완료된 걸 확인할 수 있다.

 

실제로 프로젝트를 진행하면서 API가 완성되지 않고 어떤 데이터가 응답으로 넘어올지만 알았던 상황이 있었다.

그 때는 데이터 모킹하는 방법을 떠올리지 못해서 그냥 변수에 데이터를 넣어두고 사용했던 기억이 난다.

개발하면서 MSW를 적용했다면 API 연동하는데 시간을 조금 덜 쓸 수 있지 않았을까 라는 생각이 든다.