프론트엔드/Testing

Testing on React (1)

lerrybe 2022. 12. 5. 16:32

 

 

공부한 내용을 기록합니다. 잘못된 내용이 있으면 댓글 남겨주시면 감사하겠습니다.

 

 

 

React 에서의 테스트, 오늘 살짝 맛보자!

이미지 출처: https://eatnug.github.io/frontend/modern-react-testing/first/

🧪 출처 먼저 기재합니다.

https://learn-react-test.vlpt.us/#/

https://ykss.netlify.app/translation/unit-testing-with-jest-react-and-typescript/?utm_source=substack&utm_medium=email

https://meetup.toast.com/posts/174

https://www.daleseo.com/react-testing-library/


🧪 Test Overview

💡 테스트란?

  • 보통 QA (Quality Assuarance) 과정에서 하는 행동을 살펴보면 이해가 쉬울 것
    • 서버의 API를 호출하고 기대값을 확인하는 일
    • 마크업이 끝난 이후에 디자인 시안과 비교해보는 일

https://kmong.com/gig/384090

  • 우리가 작성한 코드가 의도된 대로 잘 동작하는지 확인하고 검증하는 작업
  • 어플리케이션이 요구 사항에 맞게 동작하는지 검증하는 행위
  • 결과가 기대한 바와 일치해서 나오는지 확인하는 과정
  • 🌟입력(input)출력(output)🌟의 관점

 

📪 [1] 테스트를 하는 이유

  • 소프트웨어가 최종 사용자의 요구사항을 충족 시키는지 확인할 수 있습니다.
  • 프로그램이 실패할 가능성을 낮춰 안정적인 소프트웨어 생산을 가능하게 합니다
  • 소프트웨어의 품질과 사용자 경험을 향상시킬 수 있습니다.
  • 그 밖에 여러분이 생각하시는 장점들이 바로 테스트를 하는 이유입니다.

💡 프론트엔드에서의 테스팅

📪 [1] 백엔드에서의 테스트

https://yceffort.kr/2021/10/how-to-write-good-javascript-test-code
 
 
  • 입력을 넣었을 때 기대한 출력이 나오는지의 검증이 바로 테스트
  • 백엔드는 입력값(예:HTTP 요청)과 출력값(예:HTTP 응답)이 데이터(예:JSON 형태)로 대부분 검증이 가능
  • 그렇다면 프론트엔드에서는 어떻게?

 

 

📪 [2] 프론트엔드에서의 테스트

  • 프론트엔드에서의 입출력은, 백엔드에서처럼 명확하지 않다.
  • 단순 데이터 형태로 입력값과 출력값의 모든 검증이 불가능하기 때문에 모호하게 느껴질 수 있다.

  • 입력 예시 (x값)
    • 데이터
    • DOM 이벤트: 마우스, 키보드, 터치 등의 입력 이벤트
    • 라우팅/IO: URL 변경, 네트워크/로컬 파일, 로컬 스토리지/쿠키
    • 그 외 사용자의 액션(action)
  • 출력 예시 (f(x)값)
    • 코드 관점: HTML, CSS
    • 사용자 관점: 브라우저가 렌더링한 화면(픽셀 정보)
    • 사용자의 액션에 따른 화면의 변화
    • 시각적 요소인 경우가 많음

더 많은 입출력이 있을 수 있겠죠?


💡 우리는 이미 테스팅을 하고 있다.

사실 우리는 이미 테스트를 하고 있습니다. (두둥탁)

 

 

📪 수동 테스팅

  • 우리가 구현한 기능 직접 사용하고 관찰해보기

우리가 작성했던 TodolistLogin 컴포넌트가 올바로 동작하는지 살펴보자!

import React, { ChangeEvent, MouseEvent, FormEvent, ReactElement, useState } from 'react';

import { login } from '../../api/auth';
import { LoginUserForm } from '../../interface/user';
import './Login.scss';

interface LoginProps {
    onClickSignUpBtn: (e: MouseEvent<HTMLButtonElement>) => void;
}

const Login = ({ onClickSignUpBtn }: LoginProps): ReactElement => {
    const [formData, setFormData] = useState<LoginUserForm>({
        username: '',
        password: '',
    });

    const onChangeFormData = (e: ChangeEvent<HTMLInputElement>) => {
        const name = e.target.name;
        const value = e.target.value;
		
        setFormData({
            ...formData,
            [name]: value.trim(),
        });
        
        console.log(formData);
    };

    const onSubmit = (e: FormEvent<HTMLFormElement>) => {
    	console.log("submit이 잘 동작합니다.");
        e.preventDefault();
        login(formData);
    };

    return (
        <div className="template-container">
            <div className="head-container">
                <h1>로그인</h1>
            </div>
            <form className="login-form" onSubmit={onSubmit}>
                {/* 1️⃣ target 1 */}
                <input
                    required
                    autoComplete="username"
                    autoFocus
                    type="text"
                    id="username"
                    name="username"
                    value={formData.username}
                    onChange={onChangeFormData}
                    placeholder="아이디"
                />
                <input
                    required
                    autoComplete="current-password"
                    type="password"
                    id="password"
                    name="password"
                    value={formData.password}
                    onChange={onChangeFormData}
                    placeholder="비밀번호"
                />
                {/* 2️⃣ target 2 */}
                <button type="submit">로그인</button>
                <button name="signup" onClick={onClickSignUpBtn}>
                    회원가입
                </button>
            </form>
        </div>
    );
};

export default Login;

 

 

 

1️⃣ input 태그의 동작

 

value값이 변할 때마다 change 이벤트 리스너에 걸려있는 onChangeFormData가 올바로 동작하는가? 

  1. ✅ console.log(formData) 해본다. 콘솔이 찍힌다.
  2. ✅ 음… 잘 되는군.
  3. ✅ 그 밖에 테스트하고 싶은 것 확인
  4. ✅ 키보드 입력으로 화면에 보이는 input 태그에 값을 넣어본다.

 

 

2️⃣ button 태그의 동작

 

  1. ✅ 우리가 적용해준 css는 예쁘게 적용되고 있니?
  2. ✅ submit 이벤트 리스너에 걸려있는 함수가 올바로 동작하는가?
  3. ✅ form submit 시 우리가 작성한 로그가 콘솔에 잘 찍힌다.
  4. ✅ 백엔드와 연결되어 있는 경우에는 reponse 값이 제대로 잘 온다.
  5. ✅ 그 밖에 테스트하고 싶은 것 확인
  6. ✅ 음… 잘 되는군.

 

기존에 우리는 이런 흐름의 과정을 거쳐 우리가 작성한 코드가 정상적으로 동작 하는지 확인했습니다.

그런데 이렇게 우리가 만든 프로젝트의 모든 기능을 하나하나 수동으로 확인하는 것은 정말 번거롭습니다!!

프로젝트의 규모가 커질 수록 특히 어려운 일이고, 모든 동작과 버그를 우리가 확인하고 잡아낼 수도 없습니다.

그래서 등장한 것이 자동화 테스트!

 


💡 자동화 테스팅

 

📪 [1] 자동화 테스팅이란?

 

테스트를 해주는 코드를 작성해서 테스트 시스템이 자동으로 확인해 줄 수 있게 하자는 것

  • 사전에 준비된 테스트 코드를 실행하는 장치에 의해 테스트 코드가 수행됨
  • 자바스크립트 테스팅 도구의 종류
    • Karma
    • Jasmine
    • Jest
    • Chai
    • Mocha 등

 

📪 [2] 자동화 테스팅의 장점

  • 기능 구현의 측면
    • 우리가 작성한 코드가 기존의 기능들에 영향을 끼쳐 오작동하게 하는 것을 아주 효과적으로 방지
    • 새로운 기능이 추가되거나 기존 기능이 변경 되었을 때도 테스트 값이 제대로 나온다면 기능들이 제대로 동작한다는 것을 보장
  • 편리한 리팩토링, 리팩토링 후 정상 동작 확인
    • 테스트 코드를 짜두고 리팩토링을 하게되면 리팩토링 하기 전 과 후의 결과가 동일하다는 것을 보장함
    • 예상치 못한 문제를 잡을 수 있음

 

⚠️ 테스트 코드를 작성한다고 해서 버그가 발생하지 않는 것은 아닙니다.

그러나 버그에 대한 발견 및 유지보수가 쉬워질 가능성이 높아지겠죠!


💡 TDD란?

 

테스트 주도 개발, TDD (Test Driven Development)

TDD cycle
 

테스트 케이스를 검증하는 기능 구현 코드보다 테스트 케이스를 먼저 작성하는 개발 흐름을 말합니다.

테스트 주도 개발의 사이클은 아래와 같습니다.

 

 

📪 [1] TDD 사이클

  • 🔴 빨강(Red)
    • 문제를 정의한다.
    • 테스트 코드를 실제 코드보다 먼저 작성한다.
    • 아직 구현된 내용이 없으므로 테스트가 실패한다. (테스트 케이스를 만들고, 실패시킨다.)
  • 🟢 녹색(Green)
    • 어떤 방법으로든 정의한 문제를 해결한다.
    • 테스트 케이스를 통과시킨다, 즉 테스트 케이스에 맞게 기능을 구현한다.
    • 퀄리티가 어떻든, 일단 테스트를 통과할 수 있는 일단 최소한의 코드를 작성한다.
  • ⚪️ 리팩토링(Refactor)
    • 코드를 변경하여 중복을 제거한다.
    • 불필요하고 비효율적인 코드를 개선한다.
    • 테스트의 결과가 변하지 않게 코드를 개선한다.

 

 

📪 [1-1] TDD 흐름의 예시 살펴보기

  • Todo 입력을 받을 TodoForm을 만들어보자.
  • 코드 작성하는 방법에 대해서는 설명한 게 없으므로 흐름만 살펴보시면 됩니다.

 

🚀 문제상황

???: 이거 만들어주세요!

간단한 input과 button

 
 
 

0️⃣ 아무 내용도 없는 TodoForm 준비

// TodoForm/index.tsx

import React from 'react';

const TodoForm = () => {
  return <div />;
};

export default TodoForm;

 

STEP 1️⃣ 🔴 빨강(Red)

  • 통과하지 않을 테스트 코드 작성
// TodoForm/TodoForm.test.tsx

import { render } from '@testing-library/react';
import React from 'react';
import TodoForm from './index';

describe('TodoForm Component', () => {
  it('할 일 입력 input 태그가 있다.', () => {
    const { getByPlaceholderText } = render(<TodoForm />);
    getByPlaceholderText('할 일을 입력하세요'); // input 이 있는지 확인
  });

	it('등록 버튼이 있다.', () => {
    const { getByText } = render(<TodoForm />);
    getByText('등록'); // button이 있는지 확인
  });
});
  • 실패한다.

 

 

STEP 2️⃣ 🟢 녹색(Green)

  • 어떤 방법으로든 정의한 문제를 해결한다.
  • 테스트 케이스에 맞게 기능을 구현한다.
// TodoForm/index.tsx

import React from 'react';

const TodoForm = () => {
  return (
    <div>
      <input placeholder="할 일을 입력하세요" />
      <button type="submit">등록</button>
    </div>
  );
};

export default TodoForm;
  • 통과한다.

 

 

STEP 3️⃣ ⚪️ 리팩토링(Refactor)

  • 코드를 개선한다.
// TodoForm/index.tsx

import React from 'react';

const TodoForm = () => {
    return (
        <form>
            <input placeholder="할 일을 입력하세요" />
            <button type="submit">등록</button>
        </form>
    );
};

export default TodoForm;
  • 테스트가 깨지지 않았음을 확인

 

1, 2, 3의 과정을 반복하며 개발 👩‍💻

 

 

 

📪 [2] TDD의 장점

  • 무엇을 개발해야하는지, 무엇이 주어진 문제 상황인지 명확히 알고 시작할 수 있다.
  • 즉 문제를 먼저 정의한 뒤 → 문제의 해답을 찾아가는 개발 흐름
  • 완성된 코드가 문제없이 동작하는지 console.log() 등으로 수동으로 찍어볼 필요가 없다.
  • 코드 변경 후 다른 곳에 영향을 주는지 불안(?)에 떨 필요가 없다.
  • (다른 기능을 검증하는 테스트 코드가 잘 돌아간다면, 기능 동작에 대한 side effect를 주지 않은 것)
  • 코드 변경에 적극적일 수 있다!
  • 아무리 설계를 탄탄히 잘 했다고 하더라도 테스트 코드가 없다면.. 그걸 다 테스트 해볼 생각을 한다면.. (지끈)

여러모로 테스트는 필요할 수 있겠네요!!


🧪  테스트의 종류

그렇다면 FE 에서는 어떤 테스트들을 할 수 있을까요?

일단 널리 알려진 기준으로 살펴봅시다. (테스트를 분류하는 하나의 유명한 관점일 뿐이며, 법은 아닙니다!)

Kent C. Dodds의&nbsp; 테스팅 트로피

 

Write tests. Not too many. Mostly integration.

[Guillermo Rauch](https://twitter.com/rauchg) [tweeted](https://twitter.com/rauchg/status/807626710350839808) this a while back. Let's take a dive into what it means.

kentcdodds.com

 

 

테스팅은 크게

  • 정적 테스트(Static test)
  • 단위 테스트(Unit test)
  • 통합 테스트(Integration test)
  • E2E(end to end test)로 이루어져 있다.

각 테스트 별로 자주 쓰이는 도구들

 


💡 Static test (정적 테스트)

  • 설정이 쉽고 빠르며 애플리케이션을 개발하는 동안 오타 및 타입 에러를 지속적으로 포착할 수 있다.
  • TypeScript(타입 프로그래밍 언어) 및 ESLint(린트), Prettier 등을 통해 수행할 수 있다.

 

💡 Unit test (단위 테스트)

  • 단위 테스트는 보통 작은 단위의 기능을 테스트하는 메커니즘****
  • 주로 테스트하는 대상
    • 메서드, 함수들
    • 클래스
    • 컴포넌트
    • 모듈 등
  • 주로 어플리케이션의 로직들을 최대한 테스트 하는 것이 중요
  • 테스트 커버리지라는 지표를 통해 전체 코드 대비 얼마만큼 테스트가 되었는지를 정량화 할 수 있다.
  • Jest, testing-library

 

📪  단위 테스트의 예시

  • 컴포넌트가 잘 렌더링된다.
  • 컴포넌트의 특정 함수를 실행하면 상태가 우리가 원하는 형태로 바뀐다.
  • 리덕스의 액션 생성 함수가 액션 객체를 잘 만들어낸다.
  • 리덕스의 리듀서에 상태와 액션객체를 넣어서 호출하면 새로운 상태를 잘 만들어준다.

 

프로젝트의 기능을 쪼개서 테스트를 하면 각 기능이 모두 잘 작동하는지 확인 할 수는 있습니다.

그런데, 부분적으로는 완벽하지만 그 퍼즐들이 합쳐졌을 때 원하는 대로 동작하지 않을 수도 있습니다. (!)

 

https://learn-react-test.vlpt.us/#/
https://learn-react-test.vlpt.us/#/

 


💡 Integration test (통합 테스트)

  • 기능들이 전체적으로 잘 작동 하는지 확인하기 위해서 사용
  • 여러 단위(기능, 구성 요소, 클래스 등)의 조합으로 함께 의도한 대로 동작 하는지 확인
  • 주로 두 개 이상의 메서드, 함수, 클래스, 컴포넌트, 모듈 등의 조합에 대해 테스트
  • Jest, testing-library

 

📪  통합 테스트의 예시

  • 여러 컴포넌트들을 렌더링하고 서로 상호 작용을 잘 하고 있다.
  • DOM 이벤트를 발생 시켰을 때 우리의 UI 에 원하는 변화가 잘 발생한다.
  • 리덕스와 연동된 컨테이너 컴포넌트의 DOM 에 특정 이벤트를 발생시켰을 때 우리가 원하는 액션이 잘 디스패치 된다.

 

📪 단위 테스트와 통합 테스트의 차이

  • 유닛 테스트는 모듈 하나, 기능 하나, 컴포넌트 하나에 초점을 둔다.
  • 통합 테스트는 이름이 그러하듯 여러 요소들을 고려하여 작성한다.
  • 유닛 테스트는 보통 한 파일만 불러와서 진행하는 반면, 통합 테스트는 여러 요소들을 고려하는 과정에서, 여러 파일들을 불러와서 사용하게 될 수도 있다.
  • 추가적으로, 한 파일에 있는 여러 기능들을 함께 사용하는 것도 통합테스트로 간주될 수 있다.

 


💡End to end test (E2E, 종단 간 테스트)

  • 어플리케이션 전체가 제대로 동작하는지 확인한다.
  • 프론트엔드, 백엔드 그리고 데이터베이스와 그외의 모든 것들
  • 어플리케이션의 전체 맥락에서 소프트웨어를 사용하여 사용자의 행동을 시뮬레이션
    • 웹 페이지를 브라우저 위에서 로드 및 로그인 등과 같은 간단한 시나리오
    • 이메일 알림, 온라인 결제처럼 다소 복잡한 시나리오
    • 위와 같은 상황에서 다양한 사용자 흐름이 개발자가 의도한 대로 작동 하는지 확인
  • Cypress라는 도구를 사용

💡 무엇을 테스트할 것인가?

Q. 실제 프로젝트에서 ‘단위’란 어디까지인가?

A. 코드를 보는 관점에 따라 다르다.

 

Q. 각 테스트에서 뭘 테스트 해봐야 하는지 헷갈린다.

A. 테스트하는 대상의 주 관심사를 함께 고민해보자. 그게 테스트의 범위를 결정할 가능성이 높다.

 

  • [ ] 왜 테스트 해야 하는가?
  • [ ] 무엇을 테스트할 것인가?
  • [ ] 불필요한 테스트 코드를 작성하고 있지는 않은가?

이번 포스팅에서는 간략하게 테스트란 무엇인지, 어떤 종류가 있는지 등에 대해 알아봤습니다! 

다음 포스팅에서 본격적으로 코드와 함께 살펴보겠습니다.

 

 

 

+) 2편에서 이어집니다.

 

Testing on React (2)

공부한 내용을 기록합니다. 잘못된 내용이 있으면 댓글 남겨주시면 감사하겠습니다! React 에서의 테스트, 오늘 살짝 실습해보자! 🧪 출처 먼저 기재합니다. https://learn-react-test.vlpt.us/#/ https://ykss.

lerryroad.tistory.com

 

'프론트엔드 > Testing' 카테고리의 다른 글

Testing on React (2)  (0) 2022.12.05