RTL,Jest - 0(기본 원리와 왜 사용하는지?)

hoin_lee·2023년 8월 6일
0

Jest

목록 보기
1/2

소개

RTL (React Testing Library)

RTL은 라이브러리지만 완고한 형식으로 라이브러리 작성 방식이 특정환 관행을 권장한다

  • 사용자 사용 방식으로 소프트웨어를 테스트하는 것
  • 내부 구현 테스트를 대신하는 것 -> 소프트웨어 작성법을 의미
    즉, 중요한 것은 소프트웨어가 원래대로 작동하는지이다. 작성한 코드의 변경 방식과 소프트웨어가 사양에 따라 계속 작동하는 한 테스트를 통과하게 된다

테스트 라이브러리의 또 다른 완고한 부분은 접근성 마커로 요소를 찾는 것
즉, 테스트 ID를 사용하는 대신 스크린 리더와 다른 보조 기술로 요소를 찾는 것이다

RTL과Jest의 차이점?

RTL은 테스트를 위한 가상 DOM을 제공한다
브라우저 없이 테스트를 진행하면 클릭 요소와 같은 작엄을 할 때 가상 DOM이 필요로 하다
그리고 가상 DOM이 원하는 대로 작동하는지 확인할 수도 있다

반면, Jest는 테스트 러너이다
그래서 테스트를 찾고 실행하는 것과 테스트 통과 여부를 결정하는 역할을 한다

시작하기

버튼등을 가지고 테스트 라이브러리를 사용해 볼건데 일단 CRA(create-react-app)을 통해 프로젝트를 하나 만들고 시작해보자

이후 npm test를 입력해보자. npm test는 Jest에서 Watch 모드 실행으로 시작된다
Watch 모드와 테스트를 실행하면 여러 글자가 뜨는데

이런 글이 뜬다.
우선은 a를 입력해서 테스트를 실행시켜보자

테스트를 실행하며 한개의 테스트를 찾고 통과했다고 뜰것이다

근데 나는 아무것도 작성한게 없는데요? 기본적으로 CRA로 테스트를 만들었을 때 함께 제공되는 테스트로 App.test.js라는 파일에 있다
먼저 q를 입력하여 Watch모드를 종료하고 코드에디터로 살펴보면

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();
});

이런 테스트 코드를 볼 수 있다
한줄 씩 살펴보자

  • 테스트 함수에서는 첫번째로 render메서드를 실행한다
    • render메서드는 인수로 제공하는 JSX에 관한 가상 DOM을 생성한다
    • 해당 코드에서 JSX에 관한 인수는 app 컴포넌트이다
  • 렌더링된 가상 DOM에 어떻게 액세스 할까? -> screen global객체를 통해 액세스 한다
  • renderscreen global도 RTL에서 임포트해서 가져온다
  • 다음줄에서 getByText라는 screen객체에서 메서드를 실행하며 표시되는 모든 텍스트를 기반으로 DOM에서 요소를 찾는다
  • getByText옆에 작성된 인수 /learn react/i는 정규표현식으로 "learn react"라는 문자열이고 대소문자는 구분하지 않는다(i) 라는 뜻이다
  • 정규식을 사용하지 않으려면 문자열을 사용하면 된다 Learn React
    • 이유는 App.js에서 찾는 요소가 Learn React라는 요소이기 때문 -> 처음 CRA로 프로젝트를 생성했을 때 App.js를 살펴보면 해당 문자열 요소를 찾을 수 있다
  • 마지막은 단언인데 단언(Assertion)은 테스트 성공과 실패의 원인이다

만약 getByText인수의 정규식을 App.js에서 찾아볼 수 없는 문자열등으로 변경하면 어떻게 될까?

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

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

테스트는 실패해야 하고 문서에서도 볼 수 없어야 한다

당연히 실패했다는 에러를 받았다. /learn react testing library/i요소를 찾을 수 없다는 내용이다

Jest 단언(Assertion)

expect(linkElement).toBeInTheDocument();
위에서 말한 것처럼 단언은 테스트의 통과 여부를 결정한다
먼저 해당 코드를 하나씩 보자면

  • expect(linkElement) : Jest에서 전역 메서드인 expect 메서드
    • expect 메서드의 인수는 단언이 단언하는 것으로 예측에 들어맞는지 확인하기 위해 Jest에서 확인하는 것이다
  • toBeInTheDocument : 일종의 매처(Matcher)를 사용하는데 이것이 단언의 유형이다
    • 이 경우 Jest DOM에서 온 toBeInTheDocument
    • 간혹 매처에 인수가 있는데 toBeInTheDocument는 인수가 없다-> 요소가 문서에 있거나 없는 것

예시를 몇가지 봐보자
expect(element.textContent).toBe('hello');
시작은 언제나 expect로 시작하고 대상은 element.textContent이다
요소는 위에 봤던 코드처럼 미리 정의했다고 가정한 뒤 textContent는 요소의 텍스트 콘텐츠이다
그리고 매처는 toBe이다. 대상이 무엇이든 인수를 'hello'라는 매처에 일치시켜야 한다
텍스트 콘텐츠 요소를 'hello'로 예상하는 것

expect(elementsArray).toHaveLength(7);
마찬가지로 elementsArray는 미리 정의했다고 하고 toHaveLength라는 매처를 사용했다
인수는 7로 배열 요소의 길이를 7로 예상하는 것이다

jest-dom

CRA와 함께 제공되며 함께 설치된다. setupTests.js파일을 사용해 각 테스트 전에 jest-dom을 가져오기 한다
즉, 모든 테스트에서 jest-dom 매처를 사용할 수 있는 것
App.test.js 코드에서 toBeInTheDocument라는 매처를 사용했었는데 DOM을 기반으로 한 매처이며 방금 살펴본 예시에서 사용한 toBe,toHaveLength는 일반적인 매처이다

toBeInTheDocument와 같은 DOM기반의 매처는 가상 DOM에만 적용할 수 있다
다른 매처로는 DOM에서 볼 수 있는 toBeVisible()과 체크박스와 같은 toBeChecked()가 있고 훨씬 더 많다

jest 원리

2개의 인수를가진 전역 테스트 메서드가 있다

  • 첫 번째 인수는 테스트의 문자열 설명이고 jest에서 이 인수를 사용해서 테스트에 실패했을 때 어떤 테스트에 실패했는지 알려줄 수 있다
  • 두 번째 인수는 테스트 함수, jest는 테스트의 성공과 실패를 결정하기 위해 이 함수를 실행한다
  • 테스트는 테스트 함수를 실행할 때 에러가 발생하면 실패하게 된다
  • 단언은 예상이 틀렸을 때 에러가 발생하도록 한다. 그래서 테스트 함수에 에러가 없으면 테스트에 통과해서 그렇기 때문에 빈 테스트도 통과돼야 한다
    • 따라서 위에서 실행한 App.test.js 파일의 test함수의 함수 인수 내용을 전부 지우고 빈 테스트를 시작해도 통과된다
    • 에러를 발생시기면 테스트 실패

TDD

코드 작성 전에 테스트를 작성하고 테스트에 통과하도록 코드를 작성하는 것
보통 레드-그린 테스트라고 부르는데 코드 작성 전에 테스트에 실패하는 레드 테스트를 먼저 실행하고 코드 작성 후에는 통과하는 테스트로 그린 테스트를 확인하는 것이다
그래서 보통 테스트를 작성하기 전에 약간의 코드를 작성해서 테스트에 오류가 발생하지 않도록 한다

  • 보통 react 컴포넌트나 함수등을 만들지만 아무것도 하지 않는 빈 함수를 작성한다
  • 이후 테스트를 작성하면 함수가 아무것도 하지 않기 때문에 테스트에 실패한다
  • 그 다음으로 코드를 작성해서 테스트에 통과한다

따라서 테스트에 실패하면 빨간색이 표시되고 통과하면 초록색이 표시된다

왜 사용할까?

  • 테스트를 작성하는 것이 프로세스의 한 부분으로 느끼는 방식의 차이
    • 마지막에 테스트를 하는 것이 아닌 코딩 프로세스 자체의 일부분으로 인식하게 되는 것
  • 효율성
    • 애플리케이션을 실행해서 원하는대로 작동하는지 확인하면서 소프트웨어를 업데이트 하는데 이는 수동테스트이다
    • 하지만 코드 작성 전에 테스트를 작성하면 변경 후에 자동(Free)으로 다시 실행할 수 있다
    • 개발하면서 모든 테스트를 작성해두면 변경 사항이 생길 때마다 모든 테스트를 다시 실행해서 자동 회귀 테스트를 할 수 있다

RTL의 철학

일단 RTL은 테스트를 위한 가상 DOM을 생성하고 DOM과 상호 작용하기 위한 유틸리티도 제공한다
예시로

  • DOM에서 요소를 찾을 수 있거나 클릭과 같은 요소와 상호작용할 수 있다.
  • 브라우저 없이도 테스트를 가능하게 한다

유닛테스트
보통 함수나 별개의 React 컴포넌트 코드의 한 유닛 혹은 단위를 테스트하는 것으로
이 유닛이 다른 코드의 유닛과 상호 작용하는 것을 테스트하진 않는다


통합 테스트
통합 테스트는 여러 유닛이 함께 작동하는 방식을 테스트해서 유닛 간의 상호 작용을 테스트 하는 것
예를 들면 컴포넌트 간의 상호작용을 테스트하거나 마이크로 서비스 간의 상호 작용을 테스트한다


기능 테스트
소프트웨어의 특정 기능을 테스트하는 것
헷갈릴 수 있는 게 function은 영어로 함수에서 입력값을 취하고 출력값을 제공하는 소프트웨어 단위(Unit)을 의미할 수 있고 동작을 의미할 수도 있는데 이 경우 동작과 관련한 의미에 해당한다
특정 코드 함수가 아닌 소프트웨어의 일반적인 동작을 의미한다
일반적인 동작이라는게 뭔데? 한다면 데이터를 폼에 입력하고 제출을 클릭하면 소프트웨어가 특정 데이터 세트로 바르게 작동하는 기능을 확인해야 한다
여러 유닛이 있는 통합 테스트 일수도 있지만 유닛테스트인 기능 테스트 일 수도 있다
입력란에 잘못된 데이터를 입력하면 에러가 나는지 보는 테스트 일수도 있는 것
유닛 테스트에 가까울 뿐 기능 테스트이다
개념은 코드가 아닌 동작을 테스트 하는 것!이다


인수 테스트 or End to End테스트
종종 E2E 테스트라고도 하는데 이 테스트는 실제 브라우저가 필요하고 애플리케이션이 연결된 서버가 필요하다
보통 Cypress나 Selenium과 같은 특별한 도구가 필요함

profile
https://mo-i-programmers.tistory.com/

0개의 댓글