본문 바로가기
Web/React

TDD란? React Testing Library 사용법

by Jun_N 2022. 4. 25.

TDD 

왜 TDD를 해야 할까?

  • 테스트 코드가 없는 프로젝트의 경우 무엇을 개발해야 하는지 명확히 모른채 개발을 하게 되며 추후 코드 변경에 따른 버그를 예측할 수 없다.

TDD란?

TDD는 문제를 먼저 정의한 후 문제의 해답을 찾아가는 방식이다. 실패(Red), 성공(Green), 리팩토링(Refactoring) 이라 불리는 3단계를 반복한다.

  1. 실패: 문제 정의 단계로, 테스트 코드를 실제 코드보다 먼저 작성
  2. 성공: 문제 해결 단계로, 테스트를 통과할 수 있는 최소한의 코드를 작성
  3. 리팩토링: 불필요하고 비효율 적인 코드를 개선

테스트 범주

테스트 코드는 내가 만든 기능의 주요 핵심이 무엇인지 파악하고, 그 핵심에 집중해 테스트 코드를 작성하는 것이 좋다.

예를 들면, util 성격의 라이브러리인 lodash가 잘 작동하는지 확인하거나, useEffect가 mount 된 직후 호출되는지 굳이 테스트할 필요가 없다. lodash를 사용한 메서드나, useEffect안의 로직이 잘 동작하는지 확인하면 된다.

이러한 관점에서 2가지 부분으로 테스트 범주를 생각할 수 있다.

1) Business logic

서비스의 규모나 상황에 따라 각기 다른 구조로 구현한다.

2) Components

사용자에게 직접적으로 보여지는 부분이 중요하게 테스트 되어야 한다.

  1. Props가 잘 받아와 졌는가?
  2. State가 의도한 대로 잘 관리되고 있는가?
  3. Props나 State를 토대로 Component가 잘 rendering 되고 있는가?
  4. event handler가 잘 동작 하는가?
  5. lifecycle에 맞게 동작하는가?

React Testing Library

Jest 기반으로 만들어졌으며, 사용자가 컴포넌트를 사용하는 것처럼 테스트를 작성할 수 있다.

react-testing-library에서 컴포넌트를 렌더링 할 때에는 render()함수를 사용된다. 해당 함수에 render하고자 하는 component를 넣어주면, DOM을 선택할 수 있는 쿼리들과 container가 반환된다. 그 내부 쿼리 함수로 getByText가 존재하며 해당 함수를 사용하면 Text를 사용하여 원하는 dom을 선택 할 수 있다.

  • describe메서드를 사용해 무엇을 테스트 할 것인지 기술하는 Test suite를 작성한다.
  • it메서드로 실제 assertion하려는 테스트 케이스를 작성한다.
  • expect를 이용해서 assertion의 결과를 확인한다.

Cheatsheet | Testing Library

npm install --save-dev @testing-library/react

테스트코드 작성하기

  1. 실패 코드 작성하기

-의도하고자 했던 기능들을 테스트 코드로 먼저 작성한다.

describe('label이 존재하는 input componrnt', () => {
  it('label props에 "아이디"를 넣으면 label 영역에 아이디가 render 되어야 한다.', () => {
    const LABEL = '아이디';
    const { getByText } = render(<Input label={ LABEL } />);
    
    const labelElement = getByText(new RegExp(LABEL, 'i'));
    
    expect(labelElement).toBeInTheDocument();
  })
  
  .. 그외 필요하다 생각되는 테스트 코드들 추가
})
  1. 성공 코드 작성하기
import React from 'react';

interface Props {
  label: string;
}

export const Input = ({ label }: Props) => (
  <div>
    <label>{ label }</label>
  </div>
);
  1. ㅈ리팩토링
  2. 1~3 반복

카운트 테스트 예시

test('check init count', () => {
    const { getByText } = render(<Counter />);

    // +버튼 클릭
    const plusElement = getByText( '+' );
    fireEvent.click( plusElement );

    // 1이 있는지
    const countElement = getByText( '1' );
    expect( countElement ).toBeInTheDocument();
    // -버튼 클릭
    const minusElement = getByText( '-' );
    fireEvent.click( minusElement );

    // 0이 있는지
    const countElement2 = getByText( '0' );
    expect( countElement2 ).toBeInTheDocument();
});

----

좋은 테스트 원칙

좋은 테스트 원칙

React Test 정리

https://chrome-axolotl-f33.notion.site/cdd3f75a4c9b46d187859fc0484408d8?v=b1696d4913374185ac86d118a871c9e7

Cypress 예시

/// <reference types="cypress" />
import '@testing-library/cypress/add-commands';
import 'cypress-react-selector';

describe('Match Introduce', () => {
  beforeEach(() => {
    cy.visit('/');
    cy.viewport(360, 720);
  });

  it('/ 접속시 /match/introduce 접속', () => {
    cy.url().should('include', '/match/introduce');
  });

  it('팝업 이벤츠창이 뜨는지 확인', () => {
    cy.findByText('오늘은 그만 보기').should('exist');
  });

  it('닫기 버튼 클릭시 popup 제거', () => {
    cy.get('a > img').should('exist');
    cy.findByText('닫기').click();
    cy.get('a > img').should('not.exist');

  });

  it('잡다매칭 시작하기 버튼 클릭시 position으로 이동', () => {
    cy.findByText('닫기').click();
    cy.findByText('잡다매칭 시작하기').click();
    cy.url().should('include', '/match/position');
  });

  it('FAQ 링크 클릭시 notion 설명하기 이동', () => {
    cy.findByText('닫기').click();
    cy.get('.notion-link').click().as('notionClick');
    cy.wait(100);
    cy.url().should('include', 'dour-sweater-a18.notion.site');
  });
});

참고 레퍼런스

https://shylog.com/tdd-in-react-using-react-testing-library