jest는 페이스북에서 개발한 Javascript 테스트 프레임워크 이고 기본적으로 JS바탕인 프론트엔드 환경에서 많이 쓰인다. 주로 React 애플리케이션과 함께 사용되지만 Babel , TypeScript , Node , Angular , Vue 등을 사용하는 프로젝트에서 작동한다고 Jest 공식 사이트에 나와있다. 이처럼 출시 초기에는 프론트엔드에서 주로 쓰였지만 최근에는 백엔드에서도 기존의 자바스크립트 테스팅 라이브러리를 대체하고 있다. 또한 Jest는 다양한 기능과 쉬운 사용성으로 인기가 높은데 그 예로, config를 따로 설정하지 않아도 빠르게 테스팅 환경을 만들 수 있고, Jest는 라이브러리 하나만 설치하면, Test Runner와 Test Matcher 그리고 Test Mock 프레임워크까지 제공해주기 때문에 따로 다른 라이브러리를 설치하지 않아도 돼서 사용이 편리하다.
❗ Jest 이전에는 자바스크립트 코드를 테스트하라면 여러 가지 테스팅 라이브러리를 조합해서 사용하곤 했다. 예를 들어, Mocha나 Jasmin을 Test Runner로 사용하고, Chai나 Expect와 같은 Test Matcher를 사용했으며, 또한 Sinon과 Testdouble 같은 Test Mock 라이브러리도 필요했었다. 이 라이브러리들은 굉장히 유사하지만 살짝씩 다른 API를 가지고 있었기 때문에, 여러 프로젝트에 걸쳐서 일하는 자바스크립트 개발자들에게 혼란을 주기도 했다.
npm install --save-dev jest
Jest는 .test .spec가 포함되거나
__tests__
폴더 내에 있는 파일을 감지하여 테스트를 실행한다.
1. 테스트할함수파일명.test.js
2. 테스트할함수파일명.spec.js
3. tests 폴더 안에있는 files
describe(desc, func)
describe 함수는 관련된 테스트들을 그룹화하고 해당 그룹에 대한 설정을 제공하는데 사용된다. 테스트 모음 내에서 여러 개의 test 블록을 포함할 수 있습니다.
test(content, func)
test에서 테스트 케이스를 정의하는 함수이다. test 함수는 단일 테스트 케이스를 정의하고 해당 테스트 케이스를 실행한다. 각 테스트 케이스는 독립적으로 실행되며, expect 함수와 함께 사용하여 예상 결과를 검증할 수 있다.
expect(testing logic or something).Matchers(expected result)
테스트 결과를 검증하기 위해 사용된다. 테스트 코드 내에서 expect 함수를 사용하여 특정 값을 예상하고, 이를 실제로 받은 결과와 비교하여 테스트를 수행한다. expect 함수는 주로 Matchers(매처)라는 함수들과 함께 사용된다.
Jest에서 테스트 결과를 검증하기 위해 사용되는 함수들로서, expect 함수와 함께 사용된다. expect 함수로 얻은 값에 대해 예상 결과와 일치하는지를 확인하는 데에 사용된다. 일반적으로 toBe, toEqual, toMatch, toBeDefined 등의 많은 Matcher 함수들이 제공됩니다.
👇이곳에서 다양한 matcher들을 찾아볼 수 있다.
https://github.com/testing-library/jest-dom#with-typescript
공식페이지의 에시코드를 보면 위의 테스트 파일 구조를 이해할 수 있다.
// sum.ts
export const sum = (a: number, b: number) : number => {
return a + b;
}
// sum.test.ts
import {sum} from './sum';
import {describe, expect, test} from '@jest/globals';
describe('sum module', () => {
test('adds 1 + 2 to equal 3', () => {
expect(sum(1, 2)).toBe(3);
});
});
React Testing Library는 React 컴포넌트의 테스트를 더 쉽고 직관적으로 작성할 수 있도록 도와주는 라이브러리이다. React Testing Library는 Behavior Driven Test(행위 주도 테스트)를 추구하는 테스트 방법으로 사용자의 관점에서 컴포넌트를 테스트하는 방법을 강조한다. 즉, 컴포넌트의 내부 구현보다는 사용자가 실제로 상호작용하는 방식에 초점을 맞추어 테스트를 작성한다. 그래서 실제 브라우저에서 보여지는 DOM을 기준으로 테스트를 작성하게 된다.
기존의 Implementation Driven Test(구현 주도 테스트)에서는 주로 애플리케이션이 어떻게 작동하는지에 대해서 초점을 두어 테스트를 작성한다.
<h1 class="title">Hello World</h1>
이런 코드가 있다면,
Implementation Driven Test(구현 주도 테스트)는 h1이라는 태그가 쓰였고, title 이라는 클래스가 사용되었는지 여부를 테스트합니다. 하지만,
RTL은 h1 태그를 사용하는지보다 Hello World 메시지가 브라우저에 노출이 되는지 파악하는 것을 더 중요하다고 본다. 따라서, 사용자에게 어떤 컨텐츠가 현재 보이고, 사용자가 어떤 이벤트를 발생시켰을 때, 그에 따라 화면에 변화가 일어나는지를 테스트한다. 이와 같이 구현보다 기능에 초점을 맞춘 테스트 방식은 신뢰도를 높임과 동시에 코드 리팩토링 시 테스트 코드 수정 빈도를 줄일 수 있다.
create-react-app으로 생성한 프로젝트는 기본적으로 React Testing Library를 사용할 수 있도록 설치가 되어 있다.
만약 CRA를 사용하지 않았다면 아래와 같이 패키지를 설치하면 된다.
❗Jest의 DOM에 특화된 matcher 애드온인 jest-dom도 같이 설치해준다.
npm i -D jest @testing-libraray/react @testing-library/jest-dom
@testing-library/jest-dom은 jest에서 사용할 수 있는 jest-dom 확장 라이브러리이다. 이 라이브러리를 설치하면 Jest 테스트 코드에서 DOM 관련 검증을 더 편리하게 할 수 있다.
또한 jest-dom을 설치하면 다양한 커스텀 matcher들을 사용할 수 있다. 커스텀 matcher를 사용하면 예상한 DOM 검증을 더 읽기 쉽고 간결하게 작성할 수 있다.
👉에드온이란?
"에드온"은 기본적인 기능에 추가적인 기능이나 확장을 제공하는 소프트웨어나 도구를 의미한다. 주로 프로그램이나 웹 브라우저에서 사용자가 특정 기능을 더 추가하거나 확장하고 싶을 때 에드온을 설치하여 이용한다.
jest에서 사용되는 @testing-library/jest-dom 역시 Jest의 기본 기능을 확장하여 DOM 검증을 더 편리하고 가독성있게 작성할 수 있도록 제공하는 에드온이라고 할 수 있다. 이러한 에드온들은 테스트 코드 작성을 더 편리하고 강력하게 해준다.
React Testing Library에서는 테스트할 컴포넌트를 렌더할 수 있는 API를 제공한다.
render API를 통해서 컴포넌트를 렌더링 하고 그 결과로 나온 DOM에서 Element를 탐색할 수 있는 query들을 반환한다. 그리고 렌더링 된 DOM요소를 반환한다(=container).
import { render } from '@testing-library/react';
const Button = () => (
<button>Click Me!</button>
);
const { getByText, container } = render(<Button />);
render()함수는 인자로 렌더링할 React 컴포넌트를 넘김니다. 그리고 render() 함수는 React Testing Library 제공하는 모든 쿼리 함수와 기타 유틸리티 함수 담고 있는 객체를 리턴합니다. 따라서 다음과 같이 자바스크립트의 객체 Destructuring 문법으로 render() 함수가 리턴한 객체로 부터 원하는 쿼리 함수만 얻어올 수 있습니다.
렌더링 된 DOM 노드에 접근하여 엘리먼트를 가져오는 메서드이다. 이러한 메서드들은 특정 쿼리 (query)를 사용하여 DOM 요소를 선택하고 반환한다.
메서드를 섹션 별로 나누어 보면 이렇게 된다.
get(쿼리 타입)/All(타겟의 개수)/ByRole(타겟 유형)
get - 동기적으로 처리되며 타겟을 찾지 못할 시 에러를 던진다.
find - 비동기적으로 처리되며 타겟을 찾지 못할 시 에러를 던진다.
query - 동기적으로 처리되며 타겟을 찾지 못할 시 null을 반환한다.
코드를 작성하다보면 이벤트, 비동기적인 처리가 필요하거나 해당 타겟을 찾지 못하더라도 에러를 던지지 않게끔 처리해야하는 경우가 있다. 때문에 각 경우에 맞게 쿼리 타입을 지정해줘야 한다.
React Testing Library에서 타겟(target)을 찾지 못하는 경우, 기본적으로는 에러를 발생시키지 않고 해당 요소를 찾지 못했다는 메시지를 출력한다. 이렇게 타겟을 찾지 못해도 에러를 던지지 않는 것은 테스트 코드의 실행을 중단시키지 않고, 계속해서 다음 코드를 실행하도록 해주는데 도움이 된다.
예를 들어, getByText 메서드를 사용하여 특정 텍스트를 포함하는 요소를 찾을 때, 해당 요소가 존재하지 않는 경우 에러를 발생시키지 않고 null을 반환한다. 이러한 경우, 테스트 코드가 중단되지 않고 계속해서 실행되면서 다음 테스트 코드로 넘어갈 수 있다.
따라서, React Testing Library에서는 타겟을 찾지 못해도 에러를 던지지 않게끔 처리하여 테스트 코드의 실행을 계속할 수 있도록 하고, 개발자가 필요한 경우에 적절하게 처리할 수 있도록 유연성을 제공한다. 이를 통해 테스트 코드 작성 시 더욱 유연하고 안정적인 코드를 작성할 수 있다.
👇 query 참고문서 (리스트 포함)
https://testing-library.com/docs/dom-testing-library/cheatsheet/
React Testing Library에서 screen은 테스트 환경에서 화면을 대표하는 객체다. render 함수를 호출 한 결과로 쿼리 함수를 사용할 수도 있지만, React Testing Library에서 제공하는 screen을 통해 쿼리 함수를 사용할 수도 있다. screen 객체를 사용하면 테스트 코드의 가독성과 유지보수성을 향상시키는데 도움이 되며, React Testing Library에서 권장하는 방법으로 테스트 코드를 작성하는 데에 유용하게 사용된다.
import { render, screen } from '@testing-library/react'
const Button = () => (
<button>Click Me!</button>
);
test('<Button />', () => {
render(<Button />);
screen.getByText(/click me/i);
});
두 도구는 React 내에서 테스트를 진행할 때 같이 사용되기에 상호 보완 관계라고 볼 수 있다. (엄밀히 말하자면, RTL이 Jest를 포함하는 구조) 전반적으로 Jest를 통해 기능 테스트를 진행할 수는 있지만, React의 컴포넌트를 렌더링하고 테스트하기 위해서는 몇 가지 기능이 더 필요하다. 그렇기 때문에 React 환경에서는 둘을 같이 사용하는 것이 권장된다.
Jest - 자체적인 test runner와 test util 제공
RTL - Jest + React 컴포넌트 test util 제공
출처
개발자로서 성장하는 데 큰 도움이 된 글이었습니다. 감사합니다.