React에서 테스트하기

moontag·2022년 8월 3일
0

TDD

목록 보기
1/1

리액트에서는 Testing Library, Jest를 통해 테스트 가능

  • 두 개의 역할이 각각 다르므로 함께 사용해야 함
  • Vue, Svelte에서도 사용 가능



Testing Library

create-react-app으로 설치하면 자동으로 해당 라이브러리가 같이 설치된다


Jest

: JavaScript의 Testing Framework / Test Runner

  • 테스트 파일을 자동으로 찾아 테스트 실행
  • 실행한 결과가 옳은지에 대해 함수를 이용하여 테스트 성공/실패 여부 판단



Setting

  1. 설치
npx create-react-app 생성폴더명
  1. package.json에 자동으로 설치되어 있음
"dependencies": {
    "@testing-library/jest-dom": "^5.16.4",
    "@testing-library/react": "^13.3.0",
    "@testing-library/user-event": "^13.5.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-scripts": "5.0.1",
    "web-vitals": "^2.1.4"
  },
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test",
    "eject": "react-scripts eject"
  },
  • @testing-library/jest-dom : Jest-dom 제공하는 custom matcher를 사용할 수 있게 해줌
  • @testing-library/react : 컴포넌트의 요소를 찾기 위한 query가 포함됨
  • @testing-library/user-event : click 등 사용자 이벤트에 이용함



  1. src/setupTests.js 와 src/App.test.js 에 테스트파일 자동 생성되어 있음
  • 테스트 실행 (npm run test이나 npm t )해보면
    • 성공 메시지 PASS, 테스트 건수, 테스트 걸린 시간 등 확인 가능



  1. App.test.js에 test 함수
  • test 함수
    첫번째 인자 : 테스트 내용에 대한 설명 작성
    두번째 인자 : 실행할 테스트를 함수 형태로 작성
    • 1번째 줄
      react-testing-library를 통해 render() 함수의 인자로 테스트할 컴포넌트를 전달
    • 2번째 줄
      screen의 getByText() 메서드로 render(<App />);로 가져온 App컴포넌트에 learn react라는 문자열이 있는지 확인하고 linkElement 변수에 할당.
      (i : Regular Expression으로 "i"를 붙임으로써 대소문자 구분안함)
    • 3번째 줄
      expect 함수의 인자가 document.body에 존재하는지 toBeInTheDocument 함수를 사용하여 체크.
      toBeInTheDocument 함수는 matchers 함수라고 부름.
import { render, screen } from '@testing-library/react';
import App from './App';

test('renders learn react link', () => {
  render(<App />);
  const linkElement = screen.getByText(/learn react/i);
  expect(linkElement).toBeInTheDocument();
});

App.test.js 파일에서 test, expect 함수는 Jest의 함수고, toBeInTheDocument는 jest-dom 라이브러리에 포함된 Custom matchers다.

Q, 하지만 아직 jest-dom을 import 한 적이 없는데?
A, jest-dom은 src/setupTests.js 파일 내에서 import했기 때문이다.
만약 setupTests.js에서 import 삭제하면 toBeInTheDocument 함수를 사용할 수 없다.



간단한 test 만들기

  • src/파일명.test.js or src/파일명.spec.js 생성
  • toBe함수 : matchers 함수 중 하나로 expect 함수에 지정한 값이 toBe 함수에 지정한 값과 일치하는지 체크.
  • npm run test하고 a를 선택하면 Example.test.js와 App.test.js가 모두 실행되고 PASS 출력
// src/Example.test.js
test("1 더하기 1은 2", () => {
  expect(1 + 1).toBe(2);
});
  • it함수로도 동일한 결과 출력
// src/Example.test.js
it("1 더하기 1은 2", () => {
  expect(1 + 1).toBe(2);
});
  • describe함수 : 하나의 파일에 it, test함수 여러개 포함가능
// src/Example.test.js
describe("간단한 테스트", () => {
  test("1 더하기 1은 2", () => {
    expect(1 + 1).toBe(2);
  });
  it("2 더하기 2은 4", () => {
    expect(2 + 2).toBe(4);
  });
});
  • describe 함수 블록 - Test Suites라고 부름
  • test/it 함수 블록 - Test(Test Case)라고 부름



컴포넌트 테스트하기

  • src/components폴더생성/Light.jsx생성
  • App.test.js 파일 삭제
// Light.jsx
import { useState } from "react";

function Light({ name }) {
  const [light, setLight] = useState(false);

  return (
    <div>
      <h1>
        {name} {light ? "ON" : "OFF"}{" "}
      </h1>
      <button onClick={() => setLight(true)} disabled={light ? true : false}>
        ON
      </button>
      <button onClick={() => setLight(false)} disabled={!light ? true : false}>
        OFF
      </button>
    </div>
  );
}
export default Light;
  • App.js 수정 후 npm start 해보기
import "./App.css";
import Light from "./components/Light";

function App() {
  return <Light name="스위치" />;
}

export default App;
  • components/Light.test.js 생성
// Light.test.js
import { render, screen } from '@testing-library/react';
import Light from './Light';

it('renders Light Component', () => {
	render(<Light name="전원" />);
	const nameElement = screen.getByText(/전원 off/i);
	expect(nameElement).toBeInTheDocument();
})
  • 컴포넌트 테스트하기
// Light.test.js
import { fireEvent, render, screen } from "@testing-library/react";
import Light from "./Light";

describe("컴포넌트 테스트", () => {
  it("renders Light Component", () => {
    render(<Light name="전원" />);
    const nameElement = screen.getByText(/전원 off/i);
    expect(nameElement).toBeInTheDocument();
  });
  it("off button disabled", () => {
    render(<Light name="전원" />);
    const offButtonElement = screen.getByRole("button", { name: "OFF" });
    expect(offButtonElement).toBeDisabled();
  });
  it("on button enable", () => {
    render(<Light name="전원" />);
    const onButtonElement = screen.getByRole("button", { name: "ON" });
    expect(onButtonElement).not.toBeDisabled();
  });
  it("change from off to on", () => {
    render(<Light name="전원" />);
    const onButtonElement = screen.getByRole("button", { name: "ON" });
    fireEvent.click(onButtonElement);
    expect(onButtonElement).toBeDisabled();
  });
});
  • getByText
    props로 전달된 전원이 올바르게 표시되어 있는지 확인

  • getByRole
    button을 지정하고, 버튼이 2개이므로 옵션의 name을 이용하여 OFF 버튼을 찾음. toBeDisabled로 OFF버튼이 disabled 상태인지 확인

  • expect(onButtonElement).not.toBeDisabled()
    toBedisabled 함수 앞에 not을 붙여 ON 버튼이 disabled 안된 것을 테스트

  • fireEvent
    버튼 click 등 이벤트 유무 테스트

    • fireEvent import해야 함
    • fireEvent의 click 메서드에 전달인자로 테스트할 요소 전달





profile
터벅터벅 나의 개발 일상

0개의 댓글