Testing React

zimablue·2023년 10월 10일

testing

목록 보기
1/4

리액트 테스트에 사용되는 도구들


RTL(React Testing Library)

Testing Library의 일부로 테스트를 위한 가상 DOM을 생성하고 DOM과 상호 작용하기 위한 유틸리티도 제공합니다.
예를 들어, 브라우저 없이 테스트를 진행하면 DOM에서 요소를 찾을 수 있거나 클릭 요소와 같은 상호 작용 할 때 가상 DOM이 필요합니다.
그럴 때 가상 DOM이 원하는 대로 작동하는지 확인할 수도 있습니다.



Jest, Vitest

테스트 러너이기 때문에 테스트를 찾고 실행하는 것과 테스트 통과 여부를 결정하는 역할을 합니다.





Testing Library 기본 예제


create-react-app을 통해 생성한 react앱에 기본으로 작성되어 있는 테스트를 실행해보려고 합니다.


  1. react앱을 생성합니다.
// 새로운 react앱을 생성

npx create-react-app

  1. npm test 명령어를 통해 test를 실행합니다.
// package.json

{
  "scripts": {
    "test": "react-scripts test",
  },
}

  1. 다음과 같은 화면에서 a를 입력하여 Jest의 Watch 모드를 실행합니다.
No tests found related to files changed since last commit.
Press `a` to run all tests, or run Jest with `--watchAll`.

Watch Usage
 › Press a to run all tests.
 › Press f to run only failed tests.
 › Press q to quit watch mode.
 › Press p to filter by a filename regex pattern.
 › Press t to filter by a test name regex pattern.
 › Press Enter to trigger a test run.

  1. 결과창을 확인하고 q를 입력하여 Jest의 Watch 모드를 종료합니다.
 PASS  src/App.test.js
  ✓ renders learn react link (16 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        1.778 s, estimated 4 s
Ran all test suites.

Watch Usage: Press w to show more.

위의 프로세스로 어떤 것을 test 한 것인지 아래에서 살펴보겠습니다.



리엑트의 App.test.js 예시

create-react-app을 통해 react앱을 생성하면 기본적으로 App.test.js이라는 파일이 있습니다.
App.test.jstest라는 함수를 가지고 있습니다.

// App.test.js

import { render, screen } from '@testing-library/react';
import App from './App';

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

테스트 함수에서, 첫 번째로 react-testing-library를 이용해서 render 메서드를 실행합니다.
render 메서드는 인수로 제공하는 JSX에 관한 가상 DOM을 생성하며, 생성된 가상 DOM에는 screen global 객체로 접근합니다.

react-testing-library를 이용해서 screen객체의 특정 Query 메서드를 사용해서 요소에 접근할 수 있습니다.
예제에서는 getByText 메서드는 인수를 DOM에서 표시되는 모든 텍스트를 기반으로 요소를 찾습니다.

expect 부분은 jest를 이용하는 것이며 자세한 설명은 아래에 나올 단언 부분에 있습니다.



단언(Assertion)

테스트 성공과 실패의 원인이 되는 부분을 말합니다.
App.test.js에선 아래의 코드가 해당합니다.

// expect(expect argument).matcher()
expect(linkElement).toBeInTheDocument();
  • expect: 단언을 시작하는데 사용

  • expect argument: 단언의 대상

  • matcher: 단언의 타입
    (Jest DOM에서 가져옴)

단언은 Jest의 전역 메서드인 expect로 시작합니다.
expect 다음은 Matcher가 오게 됩니다.
Matcher는 단언의 유형으로 여러가지를 사용할 수 있습니다.
위의 코드는 Jest DOM의 toBeInTheDocument를 사용했으며 toBeInTheDocument는 해당 element가 document으며 에 존재하는지 확인할 때 사용합니다.

toBeInTheDocument는 가상 DOM에만 사용할 수 있으며 모든 노드에 적용할 수 있는 toBetoHaveLength 같은 매처도 있습니다.


App.js에는 App.test.jsgetByText로 찾는 Learn React가 있습니다.

// App.js

import logo from './logo.svg';
import './App.css';

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <img src={logo} className="App-logo" alt="logo" />
        <p>
          Edit <code>src/App.js</code> and save to reload.
        </p>
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          // 발견
          Learn React
        </a>
      </header>
    </div>
  );
}

export default App;





React Testing Library

npm install --save-dev @testing-library/user-event

주요 API

import { render, screen, fireEvent } from "@testing-library/react";
  • render
    : 인자로 넘어온 JSX(컴포넌트)의 가상 DOM을 생성합니다.

  • screen
    : DOM에서 특정 영역을 선택하기 위한 다양한 쿼리 함수를 제공합니다.
    쿼리 함수를 사용해서 화면에 렌더링된 컴포넌트의 요소들에 접근할 수 있습니다.
    스크린을 사용하려면 글로벌 DOM 환경이 필요합니다.
    jest를 사용하는 경우 테스트 환경을 jsdom으로 설정하면 글로벌 DOM 환경이 제공됩니다.

  • fireEvent
    : 쿼리 함수로 선택된 영역을 대상으로 특정 사용자 이벤트를 발생시키기 위해서 사용합니다.
    현재는 fireEvent 보다 아래에 나올 userEvent를 사용합니다.



Queries

DOM 요소에 접근하고 상호작용하기 위한 함수 집합을 나타냅니다.
테스트 코드에서 특정 DOM 요소를 선택하거나 확인하는 데 사용됩니다.

command[All]ByQueryType
  • command
    • get
    • query
    • find
  • [All]
    • 제외 or 포함
  • QueryType
    • Role
    • AltText
    • Text
    • Form element
      • PlaceholderText
      • LabelText
      • DisplayValue

Summary Table


Single Element

  • get...
    : 특정 조건에 해당하는 요소가 없으면 에러를 던지고, 하나의 요소가 존재하면 해당 요소를 반환합니다.
    여러 개의 요소가 존재하면 에러를 던집니다. (재시도 불필요)

  • query...
    : 특정 조건에 해당하는 요소가 없으면 null을 반환하고, 하나의 요소가 존재하면 해당 요소를 반환합니다.
    여러 개의 요소가 존재해도 첫 번째 요소만 반환합니다. (재시도 불필요)

  • find...
    : 프로미스를 반환합니다. 이 프로미스는 특정 조건에 해당하는 요소가 나타날 때까지 기다린 후, 해당 요소를 반환합니다.
    여러 개의 요소가 존재해도 첫 번째 요소만 반환합니다. (비동기로 재시도)
    이 프로미스는 요소를 찾으면 해결(resolve)되며, 찾지 못하거나 기본 타임아웃인 1000ms 이후에도 찾지 못하면 거부(reject)됩니다.

test("", async() => {
  render(<UserList user={users} />);
  screen.debug();
  const titleEl = await screen.findByRole("heading", {
  	name: "사용자 목록",
  }, {
  	timeout: 2000,
  });
  // 실패할 경우 이전 debug와의 차이를 보며 실패한 이유를 확인하기 위해 사용
  screen.debug(); 
  expect(titleEl).toBeInTheDocument();
})

Multiple Elements

  • getAllBy...
    : 특정 조건에 해당하는 모든 요소를 배열로 반환하며, 요소가 없으면 에러를 던집니다. (재시도 불필요)

  • queryAllBy...
    : 특정 조건에 해당하는 모든 요소를 배열로 반환하며, 요소가 없으면 빈 배열을 반환합니다. (재시도 불필요)

  • findAllBy...
    : 프로미스를 반환합니다. 이 프로미스는 특정 조건에 해당하는 모든 요소가 나타날 때까지 기다린 후, 요소들을 배열로 반환하며, 요소가 없으면 에러를 던집니다. (비동기로 재시도)
    프로미스가 찾아진 요소들의 배열로 해결(resolve)되며, 요소를 찾지 못하거나 기본 타임아웃인 1000ms 이후에도 찾지 못하면 거부(reject)됩니다.
    또한, findBy 시리즈의 함수들은 getBy* 쿼리와 waitFor 함수의 조합으로 생각할 수 있습니다. 즉, 해당 요소를 찾을 때까지 대기하고, 찾은 후에는 반환됩니다. waitFor 옵션은 마지막 인자로 전달되며, 특정 상황이나 조건을 대기할 때 사용됩니다.


ByLabelText

주어진 텍스트와 일치하는 label 또는 aria-label을 가진 요소를 찾습니다.

// JSX 예시
<label htmlFor="username">Username:</label>
<input id="username" aria-label="Username" />

// Testing Library Queries
const element = screen.getByLabelText('Username');

ByPlaceholderText

주어진 입력 placeholder 값과 일치하는 요소를 찾습니다.

// JSX 예시
<input placeholder="Enter your email" />

// Testing Library Queries
const element = screen.getByPlaceholderText('Enter your email');

ByText

주어진 텍스트와 일치하는 요소를 찾습니다.

// JSX 예시
<div>Hello, World!</div>

// Testing Library Queries
const element = screen.getByText('Hello, World!');

ByDisplayValue

주어진 폼 요소의 현재 값과 일치하는 요소를 찾습니다.

// JSX 예시
<input type="text" value="zima" />

// Testing Library Queries
const element = screen.getByDisplayValue('zima');

ByAltText

주어진 이미지 alt 속성 값과 일치하는 이미지 요소를 찾습니다.

// JSX 예시
<img src="image.jpg" alt="A beautiful landscape" />

// Testing Library Queries
const element = screen.getByAltText('A beautiful landscape');

ByTitle

주어진 title 속성 값과 일치하는 요소를 찾습니다.

// JSX 예시
<svg>
  <title>Important Information</title>
  <circle cx="50" cy="50" r="30" />
</svg>

// Testing Library Queries
const element = screen.getByTitle('Important Information');

ByRole

주어진 aria role과 일치하는 요소를 찾습니다.

WAI-ARIA Roles

  • <h1> ~ <h6>: "heading"
  • <button>: "button"
  • <a>: "link"
  • <checkbox>: "checkbox"
  • <radio>: "radio"
  • <select>: "combobox"
  • <img>: "img"
  • <textarea>: "textbox"
  • <input> (type이 search인 경우): "searchbox"
  • <input> (type이 range인 경우): "slider"
  • <progress>: "progressbar"
  • <ul>, <ol>: "list"
  • <li>: "listitem"
  • <nav>: "navigation"
  • <menu>: "menu"
  • <form>: "form"
  • <textarea>, <input> (type이 text, password, email, tel, url, number인 경우): "textbox"
  • <input> (type이 checkbox, radio, submit, reset, button인 경우): 각각 "checkbox", "radio", "button"
// JSX 예시
<button>Close</button>

// Testing Library Queries
// name은 text 노드를 가진 요소를 선택
const button = screen.getByRole('button', { name: 'Close' });

<img />의 경우 namealt를 나타냅니다.


ByTestId

주어진 data-testid 속성 값과 일치하는 요소를 찾습니다.

// JSX 예시
<button data-testid="submit-button">Submit</button>

// Testing Library Queries
const button = screen.getByTestId('submit-button');

어떤 쿼리를 사용해야 할까요?

테스트는 사용자들이 소프트웨어를 사용하는 방식을 모방해야 합니다.

  1. Queries Accessible to Everyone
    : 화면을 쳐다보고 있는 사람에게든 스크린 리더 등의 보조 기술을 사용하고 있는 사람이건 간에 모두가 액세스할 수 있는 쿼리를 사용하는 게 좋습니다.
    ex) getByRole, getByLabelText, getByPlaceholderText, getByDisplayValue

  2. Semantic Queries
    : 브라우저와 보조 기술의 사이의 일관성이 다소 떨어지기 때문에 잘 선호되지 않습니다.
    ex) getByAltText, getByTitle

  • 어떤 브라우저는 alt 속성을 이미지 설명으로만 사용하는 반면, 다른 브라우저는 도움말 텍스트로도 사용할 수 있습니다. 어떤 보조 기술은 alt 속성을 읽어주는 반면, 다른 보조 기술은 무시할 수 있습니다.
  • 어떤 브라우저는 title 속성을 툴팁으로 표시하는 반면, 다른 브라우저는 무시할 수 있습니다. 어떤 보조 기술은 title 속성을 읽어주는 반면, 다른 보조 기술은 무시할 수 있습니다.
  1. Test IDs
    : 사용자들이 테스트 ID와 상호작용할 일은 절대 없기 때문에 최후의 수단으로만 사용되어야 합니다.
    ex) getByTestId



Custom matchers

특정 라이브러리나 프레임워크에서 제공하거나 사용자가 직접 만들어 추가할 수 있는 매처들입니다.
특정 도메인이나 컴포넌트 라이브러리에 특화된 검증을 수행하는 데 사용됩니다.

React Testing Library에서 제공하는 Custom Matchers

  • toBeDisabled
    : 대상 요소가 비활성화(disabled)되었는지 확인합니다.

  • toBeEnabled
    : 대상 요소가 활성화(enabled)되었는지 확인합니다.

  • toBeEmptyDOMElement
    : 대상 DOM 요소가 비어있는지 확인합니다.

  • toBeInTheDocument
    : 대상 DOM 요소가 실제 DOM에 존재하는지 확인합니다.

  • toBeInvalid
    : 대상 요소가 유효하지 않은 상태(invalid)인지 확인합니다.

  • toBeRequired
    : 대상 요소가 필수(required)인지 확인합니다.

  • toBeValid
    : 대상 요소가 유효(valid)한 상태인지 확인합니다.

  • toBeVisible
    : 대상 요소가 화면에 보이는(visible)지 확인합니다.

  • toContainElement
    : 대상 요소가 특정 자식 요소를 포함하고 있는지 확인합니다.

  • toContainHTML
    : 대상 요소가 특정 HTML을 포함하고 있는지 확인합니다.

  • toHaveAccessibleDescription
    : 대상 요소에 접근 가능한 설명이 있는지 확인합니다.

  • toHaveAccessibleErrorMessage
    : 대상 요소에 접근 가능한 오류 메시지가 있는지 확인합니다.

  • toHaveAccessibleName
    : 대상 요소에 접근 가능한 이름이 있는지 확인합니다.

  • toHaveAttribute
    : 대상 요소가 특정 속성을 가지고 있는지 확인합니다.

  • toHaveClass
    : 대상 요소가 특정 클래스를 가지고 있는지 확인합니다.

  • toHaveFocus
    : 대상 요소가 포커스를 가지고 있는지 확인합니다.

  • toHaveFormValues
    : 대상 요소가 특정 폼 값들을 가지고 있는지 확인합니다.

  • toHaveStyle
    : 대상 요소가 특정 스타일을 가지고 있는지 확인합니다.

  • toHaveTextContent
    : 대상 요소가 특정 텍스트 내용을 가지고 있는지 확인합니다.

  • toHaveValue
    : 대상 요소의 값이 특정 값과 일치하는지 확인합니다.

  • toHaveDisplayValue
    : 대상 요소가 특정 표시 값과 일치하는지 확인합니다.

  • toBeChecked
    : 대상 요소가 체크(checked)된 상태인지 확인합니다.

  • toBePartiallyChecked
    : 대상 요소가 부분적으로 체크된 상태인지 확인합니다.

  • toHaveRole
    : 대상 요소가 특정 역할(role)을 가지고 있는지 확인합니다.

  • toHaveErrorMessage
    : 대상 요소가 특정 에러 메시지를 가지고 있는지 확인합니다.



Deprecated Matchers

이전 버전에서 사용되었지만 현재 버전에서는 권장되지 않거나 더 나은 대안이 있어서 폐기된 매처들입니다.
특별한 이유 없이는 사용을 지양하고, 대신에 더 효과적이고 가독성 높은 Matchers를 사용하는 것이 좋습니다.



User Event 라이브러리

fireEvent는 DOM 이벤트를 발생시키는데 user-event는 인터랙션 전체를 시뮬레이트합니다.


userEvent.setup()

userEvent.setup()의 도입은 v.14 에서 제공되는 새로운 기능 중 하나입니다.
초기 설정 및 설정된 사용자 상호 작용에 대한 인스턴스를 반환하여 여러 테스트 간에 상태가 공유되지 않고 독립적으로 실행될 수 있으며, 보다 표현적이고 가독성 있게 테스트 코드를 작성할 수 있도록 도와줍니다.

import userEvent from "@testing-library/user-event";

const user = userEvent.setup();

user-event API는 항상 프라미스를 반환하기 때문에 비동기 처리를 해줘야 합니다.
해당 메서드가 완료될 때까지 기다렸다가 단언을 수행해야 합니다.

// async
test("Checkbox enables button", async () => {
  const user = userEvent.setup();

  render(<SummaryForm />);

  const checkbox = screen.getByRole("checkbox", {
    name: /terms and conditions/i,
  });
  const confirmButton = screen.getByRole("button", { name: /confirm order/i });

  // await
  await user.click(checkbox);
  expect(confirmButton).toBeEnabled();
});

Pointer events

클릭할 수 있는 요소가 아닌 divspan 등의 다른 태그로 기능과 모양을 흉내내어 구현되어있는 요소를 클릭해야 할 때가 있습니다.

pointer-events는 그래픽 요소가 포인터 이벤트의 대상이 될 수 있는 상황(있는 경우)에 대해 나타내는데, 흉내내어 구현되어 있는 요소는 pointer-events: none;속성으로 되어 있습니다.

pointer-events: none;인 요소에는 click 이벤트를 사용해도 클릭할 수 있는 요소가 아니라는 에러가 발생합니다.

그런 경우 userEvent에서 제공하는 pointerEventsCheck옵션을 사용하여 해결할 수 있습니다.

const user = userEvent.setup({
  pointerEventsCheck: PointerEventsCheckLevel.Never,
});

pointerEventsCheck 옵션을 PointerEventsCheckLevel.Never로 설정하면, user-event가 요소의 "pointer-events" 속성을 무시하고 클릭을 시도합니다.

추가적인 내용은 pointerEventsCheck에서 확인할 수 있습니다.


click(element, eventInit, options)

주어진 엘리먼트를 클릭하는 행동을 시뮬레이션하는 데 사용됩니다.

  • element
    : 클릭할 대상 엘리먼트입니다.

  • eventInit
    : 이벤트 초기화 객체로, MouseEvent 인터페이스에서 지원되는 속성을 포함합니다.
    클릭 이벤트에 대한 세부 정보를 지정하려는 경우 사용됩니다.

  • options
    : user-event에서 제공하는 여러 동작을 설정하기 위한 옵션 객체입니다.
    여기에는 특정 동작을 조정하는 데 사용되는 여러 속성이 있습니다.
    세부 항목은 Options에서 확인할 수 있습니다.

  • Pointer events options
    : 위의 링크 Options에 해당하는 내용입니다.
    만약 클릭하려는 요소가 "pointer-events: none"으로 되어 있으면, 일반적으로는 클릭이 무시되고 에러가 발생합니다.
    그렇기 때문에 요소를 클릭하려는 시도에서 에러가 발생하지 않고 무시하고자 할 때 skipPointerEventsCheck: true라는 설정을 사용할 수 있습니다.
    설정은 클릭뿐만 아니라 dblClick, hover, unhover, selectOptions, deselectOptions 등과 같은 다양한 상호작용에도 적용됩니다.
userEvent.click(elem, undefined, {skipPointerEventsCheck: true})

dblClick(element, eventInit, options)

해당 엘리먼트를 더블 클릭합니다.

  • element: 더블 클릭을 수행할 대상 엘리먼트입니다.

  • eventInit
    : 이벤트 초기화 객체로, MouseEvent 인터페이스에서 지원되는 속성을 포함합니다.
    클릭 이벤트에 대한 세부 정보를 지정하려는 경우 사용됩니다.

  • options
    : user-event에서 제공하는 여러 동작을 설정하기 위한 옵션 객체입니다.
    여기에는 특정 동작을 조정하는 데 사용되는 여러 속성이 있습니다.
    세부 항목은 Options에서 확인할 수 있습니다.


type(element, text, options)

<input> 또는 <textarea> 엘리먼트에 텍스트를 입력하는 동작을 시뮬레이트합니다.
키보드 이벤트를 연속적으로 발생시켜 엘리먼트에 텍스트를 입력하는 것을 흉내 냅니다.
주로 테스트 케이스에서 사용자의 입력 상황을 모방하는 데 유용합니다.

  • element
    : 텍스트를 입력할 대상 엘리먼트입니다.

  • text
    : 입력할 텍스트입니다.
    이 텍스트가 해당 엘리먼트에 입력됩니다.

  • options
    : user-event에서 제공하는 여러 동작을 설정하기 위한 옵션 객체입니다.
    여기에는 특정 동작을 조정하는 데 사용되는 여러 속성이 있습니다.
    세부 항목은 Options에서 확인할 수 있습니다.

  • Special characters
    : 특수한 텍스트 문자열을 사용하여 특수한 키 이벤트를 발생시킬 수 있습니다.
    자세한 내용은 Special characters에서 확인할 수 있습니다.

아래의 예제에서는 엔터 키를 눌러 새로운 줄을 생성합니다.

// 두 항복은 동일한 결과를 생성합니다.
userEvent.type(element, 'Hello,{space}World!');
userEvent.type(element, 'Hello, World!');
  • {enter}
    : Enter 키를 누릅니다.
    <textarea>에서만 새로운 줄을 삽입합니다.

  • {space}
    : 스페이스 바를 입력합니다.

  • {esc}
    : Escape 키를 누릅니다.

  • {backspace}
    : Backspace 키를 누릅니다.
    selectedRange 내의 문자를 삭제할 수 있습니다.
    현재 커서 위치에서 이전 문자를 삭제하는 동작을 나타냅니다.

  • {del}: Delete 키를 누릅니다.
    selectedRange 내의 문자를 삭제할 수 있습니다.
    {del}은 현재 커서 위치에서 다음 문자를 삭제하는 동작을 나타냅니다.

  • {selectall}
    : 특정 엘리먼트의 텍스트를 모두 선택하는 동작을 나타냅니다.
    텍스트 입력이 가능한 일반적인 텍스트 상자(<input type="text"> 또는 <textarea>) 등에는 잘 작동하지만, 이메일, 비밀번호, 숫자 등 텍스트 선택이 불가능한 경우에는 이 동작이 올바르게 작동하지 않을 수 있습니다.

  • {arrowleft}
    : 왼쪽 화살표 키를 누릅니다.

  • {arrowright}
    : 오른쪽 화살표 키를 누릅니다.

  • {arrowup}
    : 위쪽 화살표 키를 누릅니다.

  • {arrowdown}
    : 아래쪽 화살표 키를 누릅니다.

  • {home}
    : Home 키를 누릅니다.

  • {end}
    : End 키를 누릅니다.

  • {shift}
    : Shift 키를 누릅니다.
    (이 특수 문자가 입력된 이후에 대문자로 변환되지 않습니다.)
    일반적으로 Shift 키를 누르면 알파벳 문자를 입력할 때 대문자로 변환됩니다.
    하지만 {shift}가 입력된 이후에는 대문자로 변환되지 않습니다.

  • {ctrl}
    : Control 키를 누릅니다.
    (Modifier: ctrlKey)

  • {alt}
    : Alt 키를 누릅니다.
    (Modifier: altKey)

  • {meta}
    : OS(예: Windows 키 또는 Command 키) 키를 누릅니다.
    (Modifier: metaKey)

  • {capslock}
    : Caps Lock 키를 누릅니다.
    Caps Lock을 활성화하는 것처럼 동작하며, 사용 중인 운영 체제에 따라 다르게 동작할 수 있습니다.
    {capslock}은 키가 눌릴 때(keydown)와 키가 떼어질 때(keyup) 양쪽 이벤트를 발생시키며, 이는 사용자가 실제로 Caps Lock 키를 클릭하여 Caps Lock을 활성화하는 것을 모방합니다.
    Caps Lock 키가 활성화되면 눌린 키는 대문자로 인식되므로, {capslock}을 사용하면 이 동작을 테스트할 수 있습니다.


keyboard(text, options)

주어진 텍스트에 해당하는 키보드 이벤트를 시뮬레이션합니다.

  • text
    : 시뮬레이션할 키보드 입력.

  • options
    : user-event에서 제공하는 여러 동작을 설정하기 위한 옵션 객체입니다.
    여기에는 특정 동작을 조정하는 데 사용되는 여러 속성이 있습니다.
    세부 항목은 keyboardMap에서 확인할 수 있습니다.

  • 예시

userEvent.keyboard('foo') // f, o, o로 변환됩니다.

// 중괄호 {와 대괄호 \[가 특수 문자로 사용되며, 이를 참조하려면 두 번 입력해야 합니다
userEvent.keyboard('{{a[[') // {, a, [로 변환됨

// 키가 눌려진 상태를 유지하지 않고 Shift 는 f 를 누르기 이전에 입력이 풀립니다.
userEvent.keyboard('{Shift}{f}{o}{o}') // Shift, f, o, o로 변환됨

//특수 문자열은 해당 문자열 앞에 \ 백슬래쉬를 붙여 이스케이프 할 수 있습니다.
keyboard('{\\}}') // translates to: }

userEvent.keyboard('[ShiftLeft][KeyF][KeyO][KeyO]') // translates to: Shift, f, o, o

userEvent.keyboard('{shift}{ctrl/}a{/shift}') // translates to: Shift(down), Control(down+up), a, Shift(up)

// 문자열 끝에 > 를 추가하여 키를 계속 누를 수 있습니다.
userEvent.keyboard('{Shift>}A{/Shift}') // translates to: Shift(down), A, Shift(up)

keyboard('{a>}') // 누른 상태를 유지
keyboard('{a>5}') // 누른 상태를 유지해 keydown 이벤트를 5번 트리거
keyboard('{a>5/}') // keydown 이벤트를 다섯번 트리거하고 키를 해제

upload(element, file, [options])

<input type="file"> 엘리먼트에 파일을 업로드하는 동작을 시뮬레이트합니다. 이 함수를 사용하여 파일을 업로드하거나, 다중 파일 업로드를 수행할 수 있습니다.

  • element
    : 텍스트를 입력할 대상 엘리먼트입니다.

  • file
    : 업로드할 파일 또는 파일 배열.

  • options
    : user-event에서 제공하는 여러 동작을 설정하기 위한 옵션 객체입니다.
    여기에는 특정 동작을 조정하는 데 사용되는 여러 속성이 있습니다.
    세부 항목은 applyAccept에서 확인할 수 있습니다.
    applyAccept: true로 설정하면 accept 속성을 적용하고 매치하지 않는 파일은 폐기됩니다.


clear(element)

<input> 또는 <textarea> 엘리먼트의 텍스트를 선택하고 삭제하는 작업을 시뮬레이트합니다.
이 함수를 사용하여 텍스트를 지우고, 그 결과를 확인할 수 있습니다.


selectOptions(element, values, options)

<select> 또는 <select multiple> 엘리먼트에서 지정된 옵션을 선택하는 동작을 시뮬레이트합니다.
이 함수는 다음과 같은 방법으로 사용될 수 있습니다.

  • element
    : 텍스트를 입력할 대상 엘리먼트입니다.

  • values
    : 선택할 옵션 값 또는 값 배열.

  • options
    : user-event에서 제공하는 여러 동작을 설정하기 위한 옵션 객체입니다.
    여기에는 특정 동작을 조정하는 데 사용되는 여러 속성이 있습니다.
    세부 항목은 Options에서 확인할 수 있습니다.


deselectOptions(element, values, options)

<select multiple> 엘리먼트에서 특정 옵션의 선택을 해제하는 동작을 모의로 수행합니다. 이 함수는 다음과 같이 사용될 수 있습니다.

  • element
    : 텍스트를 입력할 대상 엘리먼트입니다.

  • values
    : 선택 해제할 옵션 값 또는 값 배열.

  • options
    : user-event에서 제공하는 여러 동작을 설정하기 위한 옵션 객체입니다.
    여기에는 특정 동작을 조정하는 데 사용되는 여러 속성이 있습니다.
    세부 항목은 Options에서 확인할 수 있습니다.

// 여러 옵션 동시에 선택 해제도 가능
// userEvent.deselectOptions(screen.getByRole('listbox'), ['1', '2'])

tab(options)

탭 키 이벤트를 발생시켜 문서에서 document.activeElement를 변경하는 동작을 모의로 수행합니다.
이 함수는 브라우저에서 사용자가 탭 키를 눌렀을 때와 동일한 동작을 시뮬레이트합니다.

  • options
    : Tab 이벤트 옵션.

  • shift
    : 기본값은 false이며, true로 설정하면 탭의 방향이 반대로 변경됩니다.

  • focusTrap
    : 기본값은 document이며, 탭 이벤트가 제한되는 컨테이너 엘리먼트를 지정할 수 있습니다.


hover(element, options)

주어진 엘리먼트 위에 마우스 호버 이벤트를 발생시키는 역할을 합니다.
이 함수를 사용하면 특정 엘리먼트에 마우스가 호버되었을 때의 동작을 시뮬레이트하여 테스트를 수행할 수 있습니다.

  • options
    : user-event에서 제공하는 여러 동작을 설정하기 위한 옵션 객체입니다.
    여기에는 특정 동작을 조정하는 데 사용되는 여러 속성이 있습니다.
    세부 항목은 Options에서 확인할 수 있습니다.

unhover(element, options)

특정 엘리먼트에서 마우스 호버를 제거하는 역할을 합니다.
이 함수를 사용하면 호버된 상태의 엘리먼트에서 마우스가 벗어났을 때의 동작을 시뮬레이트하여 테스트를 수행할 수 있습니다.

  • options
    : user-event에서 제공하는 여러 동작을 설정하기 위한 옵션 객체입니다.
    여기에는 특정 동작을 조정하는 데 사용되는 여러 속성이 있습니다.
    세부 항목은 Options에서 확인할 수 있습니다

paste(element, text, eventInit, options)

사용자가 입력란에 텍스트를 붙여넣는 것을 시뮬레이트하는 데 사용됩니다.
이 함수를 사용하면 특정 엘리먼트에 텍스트를 붙여넣는 테스트를 수행할 수 있습니다.

  • text
    : 붙여넣을 텍스트.

  • eventInit
    : 붙여넣기 이벤트에 대한 초기 설정으로, 주로 clipboardData와 관련된 설정을 제공하는 데 사용됩니다.

  • options
    : user-event에서 제공하는 여러 동작을 설정하기 위한 옵션 객체입니다.
    여기에는 특정 동작을 조정하는 데 사용되는 여러 속성이 있습니다.
    세부 항목은 Options에서 확인할 수 있습니다





Jest

jest-dom

Jest와 함께 사용되는 라이브러리 중 하나입니다.
create-react-app에서 Jest와 함께 설치되어지며 단언(Assertion)에서 사용하는 Matcher와 유틸리티 함수를 제공합니다.

create-react-app에서도 setupTests.js 파일에서 jest-dom을 불러오고 있습니다.

// setupTests.js

// jest-dom adds custom jest matchers for asserting on DOM nodes.
// allows you to do things like:
// expect(element).toHaveTextContent(/react/i)
// learn more: https://github.com/testing-library/jest-dom
import '@testing-library/jest-dom';



Jest Matcher

jest-dom을 통해 Matcher를 사용합니다.

Common Matchers

Jest와 같은 테스트 라이브러리에서 제공하는 기본적인 매처들로, 일반적인 테스트 시나리오에 사용됩니다.
값의 일치 여부, 객체 비교, 함수 호출 여부 등을 확인하는데 사용됩니다.

  • toBe(값)
    : 원시형 값이 정확히 일치하는지 테스트 할 때 사용합니다.

  • toEqual(값)
    : 참조형 값이 정확히 일치하는지 테스트 할 때 사용합니다.

  • toStrictEqual(값)
    : toEqual과 비슷하지만 특정 요소에 undefined가 나오는 것을 허용하지 않습니다.


Truthiness

  • toBeNull()
    : null에만 일치합니다.

  • toBeUndefined()
    : undefined에만 일치합니다.

  • toBeDefined()
    : toBeUndefined의 반대입니다.

  • toBeTruthy()
    : if 구문의 conditiontrue로 취급하는 모든 것과 일치합니다.

  • toBeFalsy()
    : if 구문의 conditionfalse로 취급하는 모든 것과 일치합니다.


if 구문의 condition이란 아래와 같습니다.

if (condition)
  statement1
  [else
   statement2]

Numbers

  • toBeGreaterThan(숫자)
    : 인자로 들어온 숫자보다 커야합니다. (초과)

  • toBeGreaterThanOrEqual(숫자)
    : 인자로 들어온 숫자보다 크거나 같아야 합니다. (이상)

  • toBeLessThan(숫자)
    : 인자로 들어온 숫자보다 작아야 합니다. (미만)

  • toBeLessThanOrEqual(숫자)
    : 인자로 들어온 숫자보다 작거나 같아야 합니다. (이하)

  • toBeCloseTo(숫자)
    : 부동 소수점 숫자가 정확히 일치하는지 테스트 할 때 사용합니다.
    부동 소수점 숫자를 toBe() 또는 toEqual()로 테스트할 경우 반올림 오류로 동작하지 않을 수 있습니다.

    자바스크립트에서 0.1 + 0.2는 0.3이 아니고 0.30000000000000004으로 계산됩니다.
    컴퓨터는 숫자를 계산할 때 2진법으로 계산합니다.
    몇몇 소수는 10진법에서 2진법으로 변환하는 과정에서 무한 소수가 되어버립니다.
    이때 저장공간의 한계가 있는 컴퓨터는 무한 소수를 유한 소수로 바꾸게 됩니다.
    이 과정에서 미세한 오차가 발생하면서 값들이 손실되거나 초과하게 됩니다.
    출처: harimad.log


Strings

  • toMatch(정규식)
    : 정규식과 비교하여 문자열을 검사할 수 있습니다.

  • stringContaining(문자열)
    : 주어지는 문자를 포함하고 있는지 테스트합니다.


Arrays and iterables

  • toContain(특정 항목)
    : 배열이나 이터러블이 특정 항목을 포함하는지 여부를 확인 할 수 있습니다.

Exceptions

  • toThrow(오류 메세지 or 정규식)
    : 특정 함수가 호출될 때 오류를 발생시키는지를 테스트합니다.
    예외를 던지는 함수는 래핑 함수 내에서 호출해야 합니다.
    그렇지 않으면 toThrow assertion이 실패합니다.





Jest Watch Mode

Jest를 실행하는 방법으로 마지막 커밋(Commit) 이후 파일의 모든 변경 사항을 확인해서 마지막 커밋 이후 변경된 파일과 연관된 테스트만 실행합니다.

리엑트의 경우 package.json에 정의되어 있는 npm test 명령어를 통해 test를 실행합니다.

// package.json

{
  "scripts": {
    "test": "react-scripts test",
  },
}



Jest 원리

Jest에서 테스트를 할 때 3개의 인수를 가진 전역 테스트 메서드test()를 사용합니다.

// App.test.js

import { render, screen, fireEvent } from "@testing-library/react";
import App from "./App";


test("첫 번째 인수", () => {});

test()의 첫 번째 인수는 테스트를 문자열로 설명하는 것이며, 어떤 테스트에 실패했는지 알려 줍니다.


두 번재 인수는 테스트 함수입니다.
테스트 함수 안의 단언은 예상이 틀렸을 때 에러가 발생시키고 에러가 발생하면 테스트가 실패하게 됩니다.

따라서 테스트 함수가 비어 있다면 에러도 발생할 수 없으므로 테스트를 통과하게 됩니다.


만약 마지막 커밋(Commit) 이후 Watch Mode를 실행했을 때 변경사항이 없다면 No tests found related to files changed since last commit.와 같은 메시지가 나타납니다.



설정

src/setupTests.js

// 각 테스트가 DOM에 렌더링해놓은 내용들을 테스트가 끝날 때 다음 테스트를 위해서 지워주기
import "@testing-library/react/cleanup-after-each";

// jest-dom가 제공하는 matcher를 Jest 테스트 러너에게 인식
import "@testing-library/jest-dom/extend-expect";





참고 자료

제련소

0개의 댓글