Jest를 사용해보자! - 2편

2023. 10. 27. 17:28React

1. Jest를 사용해보자 - 1편 에 이은 2편

 

(번역) Jest, React 및 Typescript를 사용한 단위 테스트

원문: Unit testing with Jest, React, and TypeScript 💡 소프트웨어 테스트는 무엇인가요? 소프트웨어 테스트(Software Testing…

ykss.netlify.app

  • 위의 블로그에서 간단하게 알려주셔서 이걸 활용해서 jest를 사용해보려고 한다.
  • 1편에 이은 글이기 때문에 기본 세팅을 다 해놨다고 가정했다. 만약 못했다면 1편 글을 보고 오도록 하자.
  • https://daily-dev-note95.tistory.com/91

 

2. 블로그에 있는 코드를 똑같이 작성해보자.

  • 위의 블로그를 바탕으로 코드를 작성해봤다.
// App.tsx

function App() {
  return (
    <>
      <span>learn react</span>
    </>
  )
}

export default App

export function Login() {
  return(
    <form>
      <div>
        <input type="email" name="email" placeholder="email" />
      </div>
      <div>
        <input type="password" name="password" placeholder="password" />
      </div>

      <div>
        <button type="button">Sign In</button>
        <button type="button">Sign Up</button>
      </div>
    </form>
  )
}
  • App.tsx 에 있는 불필요한 것들 다 삭제했다. 그리고 Login 이라는 function을 만들어주었다.
// App.test.tsx

import '@testing-library/jest-dom';
import React from "react";
import ReactDOM from "react-dom";
import { render, screen } from '@testing-library/react';
import App, { Login } from "../src/App";

test('render learn react link', () => {
  render(<App /> as React.ReactElement);

  const LinkElement = screen.getByText(/learn react/i);
  expect(LinkElement).toBeInTheDocument();
})

describe('Login Component Tests', () => {
  let container: HTMLElement;

  beforeEach(() => {
    container = document.createElement('form');
    document.body.appendChild(container);
    ReactDOM.render(<Login />, container);
  })

  afterEach(() => {
    document.body.removeChild(container);
    container.remove();
  })

  it('Renders all input fields correctly', () => {
    const inputs = container.querySelectorAll('input');
    expect(inputs).toHaveLength(2);

    expect(inputs[0].name).toBe('email');
    expect(inputs[1].name).toBe('password');
  })

  it('Renders all buttons correctly', () => {
    const buttons = container.querySelectorAll('button')
    expect(buttons).toHaveLength(2);

    expect(buttons[0].type).toBe('button');
    expect(buttons[1].type).toBe('button');
  })
})
  • test할 코드를 작성해주었다. 처음 작성할 때 toBeInTheDocument() 라는 메서드가 없어서 어찌된 영문인지 찾아봤는데 `@testing-library/jest-dom`을 import 해주어야 한다고 한다.
  • 위의 코드를 작성해주고 ` yarr test` 를 실행시켜주면~

yarn test 명령어를 입력하고 만난 Warning 이다. 혼란하다 혼란해

  • 내 React의 버전이 18.2.0 버전이다. Warning에 나온 글을 자세히 살펴보면
Warning: ReactDOM.render is no longer supported in React 18. Use createRoot instead. Until you switch to the new API, your app will behave as if it's running React 17. Learn more: https://reactjs.org/link/switch-to-createroot
  • React 18 버전에선 더이상 ReactDOM.render 메서드를 지원하지 않는단다. createRoot로 바꿔달란다. 
  • 바꾼 코드다~
// App.test.tsx

import React from "react";
import ReactDOM from 'react-dom/client'; <--
...

describe('Login Component Tests', () => {
  let container: HTMLElement;

  beforeEach(() => {
    container = document.createElement('form');
    document.body.appendChild(container);
    ReactDOM.createRoot(container).render(<Login />); <--
  })
...
  • 위의 코드로 변경한 후에 yarn test 명령어를 실행시켰다.

코드 변경 후 yarn test 명렁어 실행. Wanring~

  • act 함수를 사용하지 않고 DOM 요소를 업데이트하려고 시도했을 때 발생하는 경고라고 한다. act 함수를 이용해서 변경해주었다.
// App.test.tsx
  
  beforeEach(() => {
    container = document.createElement('form');
    document.body.appendChild(container);
    act(() => {
      ReactDOM.createRoot(container).render(<Login />);
    })
  })
  • 다시 yarn test 명령어를 실행하니
Warning: validateDOMNesting(): <form> cannot appear as a descendant of <form>.
  • 위 에러가 나타났다. HTML의 유요하지 않은 DOM 구조로 인해 발생하는 것이고, HTML 에서 <form> 요소는 중첩될 수 없다는 뜻이란다. GPT가 알려줬다. 
  • GPT의 추천코드로 변경시켜봤다. GPT는 form을 만들지않고 div를 만들도록 하고 있었다. 변경된 전체 코드이다.
//App.test.tsx

import '@testing-library/jest-dom';
import React from "react";
import ReactDOM from 'react-dom/client';
import { render, screen } from '@testing-library/react';
import { act } from 'react-dom/test-utils';
import App, { Login } from "../src/App";

test('render learn react link', () => {
  render(<App /> as React.ReactElement);

  const LinkElement = screen.getByText(/learn react/i);
  expect(LinkElement).toBeInTheDocument();
})

describe('Login Component Tests', () => {
  let container: HTMLElement;

  beforeEach(() => {
    container = document.createElement('div');
    document.body.appendChild(container);
    act(() => {
      ReactDOM.createRoot(container).render(<Login />);
    })
  })

  afterEach(() => {
    document.body.removeChild(container);
    container.remove();
  })

  it('Renders all input fields correctly', () => {
    const inputs = container.querySelectorAll('input');
    expect(inputs).toHaveLength(2);

    expect(inputs[0].name).toBe('email');
    expect(inputs[1].name).toBe('password');
  })

  it('Renders all buttons correctly', () => {
    const buttons = container.querySelectorAll('button')
    expect(buttons).toHaveLength(2);

    expect(buttons[0].type).toBe('button');
    expect(buttons[1].type).toBe('button');
  })
})
  • 위의 코드를 변경했더니 에러 없이 정상적으로 test를 통과했다. form 태그로 변경하고 싶은데 이건 더 찾아봐야겠다.

23.10.30 추가

  • div태그를 form 태그로 변경을 하면 안된다는 사실을 알았다. container라는 변수에 div 라는 element를 만들어둔 후, 그곳에 Login 컴포넌트를 추가하는 것이기 때문에 div를 form 태그로 변경하게 되면 중복이라는 메세지가 뜨는 것이 정상적이었다. 저땐 왜 form으로 쓰려고 했는지 ㅋㅋㅋㅋ.........