ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ(Test driven devlopment, TDD)

์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ์ „๋ฌธ์ ์œผ๋กœ ๊ฐœ๋ฐœํ•˜๋Š” ๊ธฐ์—…๋“ค์€ ์„œ๋น„์Šค์˜ ์•ˆ์ •์ ์ธ ์šด์˜๊ณผ ์„œ๋น„์Šค์˜ ํ’ˆ์งˆ์„ ํ™•๋ณดํ•˜๊ธฐ ์œ„ํ•ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๊ณ  ์žˆ๋‹ค. ๋‹ค์‹œ ๋งํ•˜๋ฉด ์„œ๋น„์Šค์˜ ํ’ˆ์งˆ์„ ํ™•๋ณดํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์ด ํ•„์ˆ˜๋ผ๋Š” ๊ฒƒ์ด๋‹ค.
ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ์ด๋ผ๋Š” ๊ฐœ๋…์ด ๋‚˜์˜ค๊ธฐ ์ „์—๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ง€๊ธˆ๊นŒ์ง€ ํ•ด์˜จ ๊ฒƒ๊ณผ ๊ฐ™์ด ์›€์ง์ด๋Š” ์†Œํ”„ํŠธ์›จ์–ด๋ฅผ ๊ฐœ๋ฐœํ•œ ํ›„ ํ•ด๋‹น ์†Œํ”„ํŠธ ์›จ์–ด์— ๋Œ€ํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•ด ์™”๋‹ค. ์ด ๋ฐฉ์‹์„ ์‚ฌ์šฉํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์€ ๋ฌธ์ œ์ ๋“ค์ด ์žˆ๋‹ค.
  • ๋‹ค๋ฅธ ์‚ฌ๋žŒ์ด ์ž‘์„ฑํ•œ ์†Œ์Šค ์ฝ”๋“œ ๋˜๋Š” ๋‚ด๊ฐ€ ์˜ˆ์ „์— ์ž‘์„ฑํ•œ ์ฝ”๋“œ์— ๋Œ€ํ•ด ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•˜๋Š”๋ฐ ์ด ์ฝ”๋“œ๊ฐ€ ์–ด๋–ป๊ฒŒ ๋™์ž‘ํ•˜๋Š”์ง€ ์•Œ ์ˆ˜ ์—†๊ฑฐ๋‚˜ ๊ธฐ์–ตํ•  ์ˆ˜ ์—†์Œ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ๋ถ„์„ํ•˜๊ณ  ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•œ๋‹ค.
  • ์ž‘์„ฑ๋œ ์ฝ”๋“œ๊ฐ€ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์‰ฝ๊ฒŒ ์ž‘์„ฑ๋˜์–ด ์žˆ์ง€ ์•Š๋‹ค. ๋”ฐ๋ผ์„œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅ ํ•˜๊ฑฐ๋‚˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์ด๋ฏธ ์ž‘์„ฑ๋œ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•  ๊ฒฝ์šฐ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ์ด๋•Œ ๊ธฐ์กด ์ฝ”๋“œ์˜ ์ˆ˜์ •์œผ๋กœ ์ธํ•ด ์˜ˆ๊ธฐ์น˜ ์•Š์€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
์ด๋Ÿฐ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์ผ„ํŠธ๋ฐฑ(Kent Beck)์€ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ๋จผ์ € ์ž‘์„ฑํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฃผ๋„ ๊ฐœ๋ฐœ๋ก ์„ ์ œ์•ˆํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค. ํ…Œ์ŠคํŠธ ์คƒ ๊ฐœ๋ฐœ ๋ฐฉ๋ฒ™๋ก ์€ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ ๋“ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž๋™ํ™”๋œ ํ…Œ์ŠคํŠธ ์‹œ์Šคํ…œ์—์„œ ๋ฏธ๋ฆฌ ์ •์˜๋œ ์‚ฌ์–‘์„ ๋ฐ”ํƒ•์œผ๋กœ

Jest ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑํ•˜๊ธฐ

Jest๋Š” ์ฃผ๋กœ JavaScript ๋ฐ TypeScript ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” JavaScript ํ…Œ์ŠคํŒ… ํ”„๋ ˆ์ž„์›Œํฌ๋กœ ํŠนํžˆ React ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ํ…Œ์ŠคํŠธ์— ์ตœ์ ํ™”๋˜์–ด ์žˆ๋‹ค. ํŽ˜์ด์Šค๋ถ(ํ˜„ ๋ฉ”ํƒ€)์—์„œ ๊ฐœ๋ฐœํ–ˆ์œผ๋ฉฐ, React ์™ธ์—๋„ ๋‹ค๋ฅธ JavaScript ํ”„๋ ˆ์ž„์›Œํฌ๋‚˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์™€๋„ ์ž˜ ์ž‘๋™ํ•œ๋‹ค.

Jest์˜ ์ฃผ์š” ์ฟผ๋ฆฌ ํ•จ์ˆ˜

describe ํ•จ์ˆ˜

describeํ•จ์ˆ˜๋Š” jest๊ฐ€ ์ œ๊ณตํ•˜๋Š” ํ•จ์ˆ˜๋กœ์จ ์—ฌ๋Ÿฌ ํ…Œ์ŠคํŠธ๋ฅผ ํ•œ ๊ทธ๋ฃน์œผ๋กœ ๋ฌถ๊ณ  ์„ค๋ช…์„ ๋ถ™์ด๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ฒซ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ๋ช…๋ น ํ”„๋กฌํ”„ํŠธ์— ํ‘œ์‹œํ•  ์„ค๋ช…
  • ๋‘๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ์—ฌ๋Ÿฌ ํ…Œ์ŠคํŠธ๋ฅผ ๊ทธ๋ฃน์œผ๋กœ ๋ฌถ์„ ์ฝœ๋ฐฑ ํ•จ์ˆ˜
describe('test index.js file', () => {

});

it ํ•จ์ˆ˜

itํ•จ์ˆ˜๋Š” ์‹ค์ œ ํ…Œ์ŠคํŠธ๊ฐ€ ์‹คํ–‰๋˜๋Š” ํ…Œ์ŠคํŠธ ๋ช…์„ธ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ฒซ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜๋Š” ํ…Œ์ŠคํŠธ ๋ช…์„ธ์˜ ์„ค๋ช…
  • ๋‘ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜์—๋Š” ์‹ค์ œ๋กœ ํ…Œ์ŠคํŠธ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ
it('sum 1+2 to equal 3', () => {
      
});

expect ํ•จ์ˆ˜

expect ํ•จ์ˆ˜๋Š” ์ฃผ๋กœ ํ…Œ์ŠคํŠธ ํ”„๋ ˆ์ž„์›Œํฌ์—์„œ ํ…Œ์ŠคํŠธ ๊ฒฐ๊ณผ๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ๋˜๋Š” ๋ฉ”์„œ๋“œ๋‚˜ ํ•จ์ˆ˜์ด๋‹ค. expect ํ•จ์ˆ˜๋Š” ํ…Œ์ŠคํŠธ ๋Œ€์ƒ์ด ํŠน์ • ์กฐ๊ฑด์„ ๋งŒ์กฑํ•˜๋Š”์ง€, ์ฆ‰ ์˜ˆ์ƒ๋œ ๋™์ž‘์„ ์ˆ˜ํ–‰ํ•˜๋Š”์ง€๋ฅผ ๊ฒ€์ฆํ•˜๋Š” ์—ญํ• ์ด๋‹ค.
it('sum 1+2 to equal 3', () => {
      expect(sum(1,2)).toBe(3);
});
  • ์—ฌ๊ธฐ์„œ sum(1,2)๋Š” ํ…Œ์ŠคํŠธ ๋Œ€์ƒ์ด ์‹ค์ œ๋กœ ์ƒ์„ฑํ•œ ๊ฐ’์ด๊ณ , 3๋Š” ๊ธฐ๋Œ€ํ•˜๋Š” ๊ฐ’์ด๋ฉฐ toBe๋Š” ์ผ์น˜ ์—ฌ๋ถ€๋ฅผ ํ™•์ธํ•˜๋Š” ๋ฉ”์„œ๋“œ์ด๋‹ค.
expect๋Š” ๋‹จ์ˆœํ•œ ๊ฐ’ ๋น„๊ต ์™ธ์—๋„ ๋‹ค์–‘ํ•œ ์ผ์น˜ ์กฐ๊ฑด์„ ์ œ๊ณตํ•œ๋‹ค. ์˜ˆ๋ฅผ ๋“ค์–ด, ๋‘ ๊ฐ์ฒด๊ฐ€ ๋™์ผํ•œ์ง€, ๋ฐฐ์—ด์ด ํŠน์ • ์š”์†Œ๋ฅผ ํฌํ•จํ•˜๋Š”์ง€, ํ•จ์ˆ˜๊ฐ€ ํŠน์ • ์˜ˆ์™ธ๋ฅผ ๋˜์ง€๋Š”์ง€ ๋“ฑ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
// ๊ฐ์ฒด๋‚˜ ๋ฐฐ์—ด์˜ ๋™๋“ฑ์„ฑ ๋น„๊ต
expect(actualValue).toEqual(expectedValue); 

// ๋ฐฐ์—ด์ด ํŠน์ • ์•„์ดํ…œ์„ ํฌํ•จํ•˜๋Š”์ง€
expect(array).toContain(item); 

// ํ•จ์ˆ˜๊ฐ€ ํŠน์ • ์˜ค๋ฅ˜๋ฅผ ๋˜์ง€๋Š”์ง€
expect(() => functionCall()).toThrow(ErrorType); 
        

@testing-library๋กœ ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ ํ•˜๊ธฐ

@testing-library๋Š” DOM ํ…Œ์ŠคํŒ… ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(DOM Testing Library)์ด๋‹ค. @testing-library๋Š” ์‚ฌ์šฉ์ž ์ค‘์‹ฌ ๋ฐฉ์‹์œผ๋กœ UI ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๋Š”๋ฐ ๋„์›€์„ ์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ด๋ฉฐ ๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ์˜ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์„ ๋„์™€์ค€๋‹ค.

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ ์ปดํฌ๋„ŒํŠธ ์„ธ๋ถ€ ๊ตฌํ˜„์‚ฌํ•ญ์„ ํฌํ•จํ•˜์ง€ ์•Š์œผ๋ฉด์„œ๋„ ์‹ ๋ขฐํ•  ์ˆ˜ ์žˆ๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์— ๋„์›€์„ ์ค€๋‹ค. ์ด๋ ‡๊ฒŒ ์ปดํฌ๋„ŒํŠธ์˜ ์„ธ๋ถ€ ๊ตฌํ˜„ ์‚ฌํ•ญ์„ ํฌํ•จํ•˜์ง€ ์•Š์€ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด ์ปดํฌ๋„ŒํŠธ์˜ ์„ธ๋ถ€ ๊ตฌํ˜„ ๋ถ€๋ถ„์„ ๋ฆฌํŒฉํ† ๋ง ํ•˜์—ฌ๋„ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•  ํ•„์š”๊ฐ€ ์—†๋‹ค, ์ด๋กœ ์ธํ•ด ํ•œ๋ฒˆ ์ž‘์„ฑํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ๊ธด ์‹œ๊ฐ„ ์œ ์ง€ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์˜ค๋žœ ๊ธฐ๊ฐ„ ์œ ์ง€ ๊ฐ€๋Šฅํ•˜์—ฌ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž์ฃผ ์ˆ˜์ •ํ•˜์ง€ ์•Š์•„๋„ ๋˜๋ฏ€๋กœ ๊ฐœ๋ฐœ ์ƒ์‚ฐ์„ฑ์„ ํ–ฅ์ƒ ์‹œ์ผœ ์ค€๋‹ค.

App.js ์ปดํฌ๋„ŒํŠธ - ๋ Œ๋”๋ง ํ…Œ์ŠคํŠธ

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

test

test ํ•จ์ˆ˜(it ํ•จ์ˆ˜์™€ ๊ฐ™์€ ์—ญํ• ์„ ํ•˜๋Š” ํ•จ์ˆ˜)๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ…Œ์ŠคํŠธ ๋ช…์„ธ๋ฅผ ์ž‘์„ฑํ•œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์ด๋‹ค.

render

๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ™”๋ฉด์— ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•จ

screen

๋ฆฌ์•กํŠธ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ‘œ์‹œ๋œ ํ™”๋ฉด์„ ์˜๋ฏธํ•œ๋‹ค.

GetByText

getByText ํ•จ์ˆ˜๋Š” ์ฃผ์–ด์ง„ ํ…์ŠคํŠธ๋ฅผ ํฌํ•จํ•˜๋Š” DOM ์š”์†Œ๋ฅผ ์ฐพ๋Š” ๋ฐ ์‚ฌ์šฉ๋œ๋‹ค. ๋ Œ๋”๋ง๋œ ์ปดํฌ๋„ŒํŠธ์—์„œ screen.GetByText๋ฅผ ํ†ตํ•ด ํ™”๋ฉด์—์„œ 'learn react'๋ผ๋Š” ๊ธ€์ž๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋” ์š”์†Œ๋ฅผ ์ฐพ๋Š”๋‹ค.
import { render, screen } from '@testing-library/react';
import App from './App';
        
test('renders Hello World text', () => {
  render(<App />);
  const textElement = screen.getByText('Hello World');
  expect(textElement).toBeInTheDocument();
});
        

toBeInTheDocument

์š”์†Œ๋ฅผ ์ฐพ์•„ jest expect().toBeInTheDocument()๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋”์— ํ‘œ์‹œ๋˜์–ด ์žˆ๋Š”์ง€ ํ™•์ธ ํ•œ๋‹ค.
render ํ•จ์ˆ˜๋Š” ๋ฉ”๋ชจ๋ฆฌ์ƒ์— ๋”์„ ๋งŒ๋“ค๊ณ  screen์„ ํ†ตํ•ด ํ•ด๋‹น ๋”์— ์ ‘๊ทผ ํ•˜๋Š” ๊ฒƒ์„ ์˜๋ฏธํ•œ๋‹ค. react-testing-library์˜ render ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ App์ด๋ผ๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋ง ํ•˜์˜€๋‹ค.

React Testing Library์˜ ์ฃผ์š” ์ฟผ๋ฆฌ ํ•จ์ˆ˜

getByText

์ฃผ์–ด์ง„ ํ…์ŠคํŠธ๋ฅผ ํฌํ•จํ•˜๋Š” ์š”์†Œ๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
ex) screen.getByText(/learn react/i)๋Š” ๋Œ€์†Œ๋ฌธ์ž๋ฅผ ๊ตฌ๋ถ„ํ•˜์ง€ ์•Š๊ณ  "learn react"๋ผ๋Š” ํ…์ŠคํŠธ๋ฅผ ํฌํ•จํ•˜๋Š” ์š”์†Œ๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
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();
});
        

getByRole

ํŠน์ • ์—ญํ• (์˜ˆ: ๋ฒ„ํŠผ, ๋งํฌ)์„ ๊ฐ€์ง„ ์š”์†Œ๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
screen.getByRole('button', { name: /submit/i })์€ "submit" ํ…์ŠคํŠธ๋ฅผ ๊ฐ€์ง„ ๋ฒ„ํŠผ์„ ์ฐพ์Šต๋‹ˆ๋‹ค.
import { render, screen } from '@testing-library/react';
import App from './App';
        
test('renders submit button', () => {
  render(<App />);
  const buttonElement = screen.getByRole('button', { name: /submit/i });
  expect(buttonElement).toBeInTheDocument();
});
        

getByLabelText

ํŠน์ • ๋ผ๋ฒจ ํ…์ŠคํŠธ์™€ ์—ฐ๊ฒฐ๋œ ์š”์†Œ(์ฃผ๋กœ ํผ ํ•„๋“œ)๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
screen.getByLabelText('Username')์€ "Username" ๋ผ๋ฒจ์„ ๊ฐ€์ง„ ์ž…๋ ฅ ํ•„๋“œ๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
import { render, screen } from '@testing-library/react';
import LoginForm from './LoginForm';
        
test('finds the username input', () => {
  render(<LoginForm />);
  const inputElement = screen.getByLabelText('Username');
  expect(inputElement).toBeInTheDocument();
});
        

getByPlaceholderText

ํŠน์ • ํ”Œ๋ ˆ์ด์Šคํ™€๋” ํ…์ŠคํŠธ๋ฅผ ๊ฐ€์ง„ ์ž…๋ ฅ ์š”์†Œ๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
screen.getByPlaceholderText('Enter your username')์€ "Enter your username" ํ”Œ๋ ˆ์ด์Šคํ™€๋”๋ฅผ ๊ฐ€์ง„ ์ž…๋ ฅ ํ•„๋“œ๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
import { render, screen } from '@testing-library/react';
import LoginForm from './LoginForm';
        
test('finds the input by placeholder', () => {
  render(<LoginForm />);
  const inputElement = screen.getByPlaceholderText('Enter your username');
  expect(inputElement).toBeInTheDocument();
});
        

getByAltText

์ฃผ์–ด์ง„ alt ํ…์ŠคํŠธ๋ฅผ ๊ฐ€์ง„ ์ด๋ฏธ์ง€๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
screen.getByAltText('Profile Picture')์€ "Profile Picture" alt ํ…์ŠคํŠธ๋ฅผ ๊ฐ€์ง„ ์ด๋ฏธ์ง€๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
import { render, screen } from '@testing-library/react';
import Profile from './Profile';
        
test('finds the profile picture', () => {
  render(<Profile />);
  const imageElement = screen.getByAltText('Profile Picture');
  expect(imageElement).toBeInTheDocument();
});
        

getByTestId

data-testid ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ ์š”์†Œ๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
screen.getByTestId('custom-element')์€ data-testid="custom-element" ์†์„ฑ์„ ๊ฐ€์ง„ ์š”์†Œ๋ฅผ ์ฐพ์Šต๋‹ˆ๋‹ค.
import { render, screen } from '@testing-library/react';
import Component from './Component';
        
test('finds element by test id', () => {
  render(<Component />);
  const element = screen.getByTestId('custom-element');
  expect(element).toBeInTheDocument();
});
        

queryByText

์š”์†Œ๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๊ฑฐ๋‚˜, ํ•˜๋‚˜๋งŒ ์กด์žฌํ•  ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์กด์žฌํ•˜์ง€ ์•Š์œผ๋ฉด null์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
getByText๋Š” ์š”์†Œ๊ฐ€ ์—†์œผ๋ฉด ์—๋Ÿฌ๋ฅผ ๋˜์ง‘๋‹ˆ๋‹ค.
import { render, screen } from '@testing-library/react';
import App from './App';
        
test('does not find non-existing text', () => {
  render(<App />);
  const textElement = screen.queryByText('Non-existing Text');
  expect(textElement).toBeNull();
});
        

findByText

๋น„๋™๊ธฐ์ ์œผ๋กœ ์š”์†Œ๋ฅผ ์ฐพ์„ ๋•Œ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ฃผ๋กœ ์š”์†Œ๊ฐ€ ๋‚˜์ค‘์— ๋ Œ๋”๋ง๋  ๊ฒฝ์šฐ์— ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ๋ฐ˜ํ™˜ ๊ฐ’์€ Promise์ž…๋‹ˆ๋‹ค.
import { render, screen } from '@testing-library/react';
import App from './App';
        
test('renders welcome message', async () => {
  render(<App />);
  const messageElement = await screen.findByText(/welcome/i);
  expect(messageElement).toBeInTheDocument();
});
        

์ฐธ๊ณ ํ• ๋งŒํ•œ ์˜ˆ์ œ๋“ค

์˜ˆ์ œ [1] - ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ Œ๋”๋ง๋˜๋Š”์ง€๋ฅผ ํ™•์ธํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค

์— ๋Œ€ํ•œ Jest ํ…Œ์ŠคํŠธ ์Šค์œ„ํŠธ๋ฅผ ์ž‘์„ฑํ•œ ๊ฒƒ์œผ๋กœ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ๋ Œ๋”๋ง๋˜๋Š”์ง€๋ฅผ ํ™•์ธํ•˜๋Š” ํ•˜๋‚˜์˜ ํ…Œ์ŠคํŠธ ์ผ€์ด์Šค๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ๋‹ค.

๊ด€๋ จ ํ‚ค์›Œ๋“œ

render

getElementByClassName

toHaveLength

toHaveAttribute

describe('<App />', ()=>{
it('renders component correctly', ()=>{      
      โ‘  const { container } = render(<App />);              
      โ‘ก expect(container.getElementByClassName('App-logo')).toHaveLength(1);       
      โ‘ข expect(container.getElementByClassName('App-logo')[0]).toHaveAttribute('src','logo.svg');
    })
})
โ‘  ์ด ์ค„์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•œ๋‹ค. render ํ•จ์ˆ˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ€์ƒ DOM์— ๋ Œ๋”๋งํ•˜๊ณ , ๋ฐ˜ํ™˜๋œ ๊ฐ์ฒด์—์„œ container๋ฅผ ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹นํ•˜์—ฌ ๊ฐ€์ ธ์˜ค๋ฉฐ container๋Š” ๋ Œ๋”๋ง๋œ DOM ํŠธ๋ฆฌ๋ฅผ ํฌํ•จํ•œ๋‹ค. โ‘ก ์ด ์ค„์€ container ์•ˆ์— 'App-logo' ํด๋ž˜์Šค๋ฅผ ๊ฐ€์ง„ ์š”์†Œ๊ฐ€ ํ•˜๋‚˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. expect ํ•จ์ˆ˜๋Š” ๊ธฐ๋Œ€๊ฐ’์„ ์„ค์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๊ณ , toHaveLength(1)๋Š” ์š”์†Œ์˜ ๊ธธ์ด๊ฐ€ 1์ธ์ง€ ํ™•์ธํ•œ๋‹ค. โ‘ข ์ด ์ค„์€ 'App-logo' ํด๋ž˜์Šค๋ฅผ ๊ฐ€์ง„ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๊ฐ€ 'src' ์†์„ฑ์„ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ, ๊ทธ ๊ฐ’์ด 'logo.svg'์ธ์ง€ ํ™•์ธํ•œ๋‹ค. toHaveAttribute('src', 'logo.svg')๋Š” ์š”์†Œ์˜ src ์†์„ฑ์ด 'logo.svg'์ธ์ง€๋ฅผ ํ™•์ธํ•œ๋‹ค.

์˜ˆ์ œ [2] - <img /> ํ…Œ์ŠคํŠธ ์ฝ”๋“œ

๊ด€๋ จ ํ‚ค์›Œ๋“œ

render

getElementsByTagName

toHaveLength

toHaveTextContext

describe('<App />', ()=>{
it('renders component correctly', ()=>{
      โ‘ const { container } = render(<App />);  
      โ‘ก expect(container.getElementsByTagName('p')).toHaveLength(1);
      โ‘ข expect(container.getElementsByTagName('p')[0]).toHaveTextContext('Edit src/App.js and save to reload.')
   })
})
โ‘  ์ด ์ค„์€ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•œ๋‹ค. render ํ•จ์ˆ˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ฐ€์ƒ DOM์— ๋ Œ๋”๋งํ•˜๊ณ , ๋ฐ˜ํ™˜๋œ ๊ฐ์ฒด์—์„œ container๋ฅผ ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹นํ•˜์—ฌ ๊ฐ€์ ธ์˜ค๋ฉฐ container๋Š” ๋ Œ๋”๋ง๋œ DOM ํŠธ๋ฆฌ๋ฅผ ํฌํ•จํ•œ๋‹ค. โ‘ก ์ด ์ค„์€ container ์•ˆ์—

ํƒœ๊ทธ๋ฅผ ๊ฐ€์ง„ ์š”์†Œ๊ฐ€ ํ•˜๋‚˜ ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. expect ํ•จ์ˆ˜๋Š” ๊ธฐ๋Œ€๊ฐ’์„ ์„ค์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๊ณ , toHaveLength(1)๋Š” ํ•ด๋‹น ์š”์†Œ์˜ ๊ธธ์ด๊ฐ€ 1์ธ์ง€ ํ™•์ธํ•œ๋‹ค. โ‘ข ์ด ์ค„์€

ํƒœ๊ทธ๋ฅผ ๊ฐ€์ง„ ์ฒซ ๋ฒˆ์งธ ์š”์†Œ๊ฐ€ 'Edit src/App.js and save to reload.'๋ผ๋Š” ํ…์ŠคํŠธ ๋‚ด์šฉ์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. expect ํ•จ์ˆ˜๋Š” ๊ธฐ๋Œ€๊ฐ’์„ ์„ค์ •ํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ๋˜๊ณ , toHaveTextContent('Edit src/App.js and save to reload.')๋Š” ํ•ด๋‹น ์š”์†Œ์˜ ํ…์ŠคํŠธ ๋‚ด์šฉ์ด ์ •ํ™•ํžˆ ์ผ์น˜ํ•˜๋Š”์ง€๋ฅผ ํ™•์ธํ•œ๋‹ค.

์˜ˆ์ œ [3] - <p/> ํ…Œ์ŠคํŠธ

describe('<App />', ()=>{
  it('renders component correctly', ()=>{
      const { container } = render(<App />);    
      expect(container).toMatchSnapshot();
    })
})
toMatchSnapshot๊ฐ€ ์‹คํ–‰๋˜๋ฉด src/snapshots/App.test.js.snap์ด๋ผ๋Š” ํŒŒ์ผ์ด ์ƒ์„ฑ๋œ ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค. ํŒŒ์ผ์„ ์—ด์–ด ๋‚ด์šฉ์„ ํ™•์ธํ•ด ๋ณด๋ฉด App ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ™”๋ฉด์— ๋ Œ๋”๋ง๋  ๋•Œ ํ‘œ์‹œ๋˜๋Š” HTML ๋‚ด์šฉ์ด ์ €์žฅ๋œ ๊ฒƒ์„ ํ™•์ธ ํ•  ์ˆ˜ ์žˆ๋‹ค.
์ €์žฅ๋œ ์Šค๋ƒ…์ƒท์€ App ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ˆ˜์ •๋˜์–ด ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๋Š” HTML ๊ตฌ์กฐ๊ฐ€ ๋ณ€๊ฒฝ๋˜๋ฉด ์—๋Ÿฌ๋ฅผ ํ‘œ์‹œํ•˜๊ฒŒ ๋œ๋‹ค. ์ด๋ ‡๊ฒŒ ์Šค๋ƒ…์ƒท์€ ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๋ณ€๊ฒฝ๋˜์—ˆ๋Š”์ง€ ๊ฐ์ง€ํ•˜๊ธฐ ์œ„ํ•œ ํ…Œ์ŠคํŠธ๋กœ ๋งŽ์ด ์‚ฌ์šฉ๋œ๋‹ค.

์˜ˆ์ œ [4] - ์Šค๋ƒ…์ƒท (ํ™”๋ฉด์— ํ‘œ์‹œ๋˜๋Š” ๋‚ด์šฉ์ด ๋ณ€๊ฒฝ ๋˜์—ˆ๋Š”์ง€ ์ฒดํฌ)

๊ด€๋ จ ํ‚ค์›Œ๋“œ

getByText

parentElement

toHaveStyleRule

Button ์ปดํฌ๋„ŒํŠธ๋Š” Props๋งŒ์„ ๊ฐ€์ง€๋Š” ๋‹จ์ˆœ ์ปดํฌ๋„ŒํŠธ์ด๋‹ค. Button ์ปดํฌ๋„ŒํŠธ๊ฐ€ ํ™”๋ฉด์— ์ž˜ ํ‘œ์‹œ ๋˜๋Š”์ง€, Props๊ฐ€ ์ž˜ ์ ์šฉ๋˜๋Š”์ง€๋ฅผ ํ™•์ธํ•˜๋„๋ก ํ•œ๋‹ค.
import React from 'react';
import { render, screen } from 'testing-library/react';
import 'jest-styled-components';
import {Button} from './index';

describe('<Button />', ()=>{
  it('renders component correctly', ()=>{
        โ‘  const { container } = render(<Button label="Button Test" />);

        โ‘ก const label = screen.getByText("button Test");
           expect(label).toBeInTheDocument();

        โ‘ข const parent = label.parentElement;
        โ‘ฃ expect(parent).toHaveStyleRule('background-color', '#304FFE');
          expect(parent).toHaveStyleRule('background-color', '#1E40FF', { modifier:'hover'});
        
        โ‘ค expect(container).toMatchSnapshot();
    })
})
โ‘  render ํ•จ์ˆ˜๋Š” ๋ฅผ ๊ฐ€์ƒ DOM์— ๋ Œ๋”๋งํ•˜๊ณ , ๋ฐ˜ํ™˜๋œ ๊ฐ์ฒด์—์„œ container๋ฅผ ๊ตฌ์กฐ ๋ถ„ํ•ด ํ• ๋‹นํ•œ๋‹ค.

โ‘ก screen.getByText๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ "Button Test"๋ผ๋Š” ํ…์ŠคํŠธ๋ฅผ ๊ฐ€์ง„ ์š”์†Œ๋ฅผ ์ฐพ๋Š”๋‹ค. expect๋กœ ํ•ด๋‹น ์š”์†Œ๊ฐ€ ๋ฌธ์„œ์— ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•œ๋‹ค.

โ‘ข label ์š”์†Œ์˜ ๋ถ€๋ชจ ์š”์†Œ(parentElement)๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.

โ‘ฃ expect๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ถ€๋ชจ ์š”์†Œ๊ฐ€ ํŠน์ • ์Šคํƒ€์ผ ๊ทœ์น™์„ ๊ฐ€์ง€๊ณ  ์žˆ๋Š”์ง€ ํ™•์ธํ•œ๋‹ค. jest-styled-components์˜ toHaveStyleRule์„ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์Œ ๋‘ ๊ฐ€์ง€ ์Šคํƒ€์ผ ๊ทœ์น™์„ ํ™•์ธํ•œ๋‹ค.
  • ๊ธฐ๋ณธ ๋ฐฐ๊ฒฝ์ƒ‰์ด #304FFE์ธ์ง€
  • hover ์ƒํƒœ์—์„œ ๋ฐฐ๊ฒฝ์ƒ‰์ด #1E40FF์ธ์ง€
backgroundColor๊ณผ hoverColor์€ ์šฐ๋ฆฌ๊ฐ€ screen.getByText๋กœ ์ฐพ์€ Label ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์•„๋‹Œ Label ์ปดํฌ๋„ŒํŠธ์˜ ๋ถ€๋ชจ ์š”์†Œ์ธ Container ์ปดํฌ๋„ŒํŠธ์— ์„ค์ •์ด ๋œ๋‹ค. ๋”ฐ๋ผ์„œ label.parentElement๋ฅผ ์‚ฌ์šฉํ•ด Label ์ปดํฌ๋„ŒํŠธ์˜ ๋ถ€๋ชจ ์š”์†Œ(Container ์ปดํฌ๋„ŒํŠธ)์— ์ ‘๊ทผํ•˜์—ฌ ๊ฐ’์ด ์ž˜ ์„ค์ • ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ ํ•œ๋‹ค.

Button ์ปดํฌ๋„ŒํŠธ๋Š” Props๋กœ BackgroundClolor๊ณผ hoverColor์ด ์„ค์ •๋˜์–ด ์žˆ์ง€ ์•Š์œผ๋ฉด ๊ธฐ๋ณธ๊ฐ’์ด ์„ค์ •๋˜๋„๋ก ๊ฐœ๋ฐœ ๋˜์—ˆ๋‹ค. backgroundColor๊ณผ hoverColor๊ฐ€ ์„ค์ •๋˜์–ด ์žˆ์ง€ ์•Š์€ ์ƒํ™ฉ์—์„œ ๊ธฐ๋ณธ๊ฐ’์ด ์ž˜ ์„ค์ •๋˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด jest-styled-components์˜ ์ƒˆ๋กœ์šด Matcher์ธ toHaveStyleRule๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ™•์ธ ํ•˜๋„๋ก ํ•œ๋‹ค.
โ‘ค ๋งˆ์ง€๋ง‰์œผ๋กœ, container์˜ ํ˜„์žฌ ์ƒํƒœ๋ฅผ ์Šค๋ƒ…์ƒท์œผ๋กœ ์ €์žฅํ•˜์—ฌ ์ดํ›„ ํ…Œ์ŠคํŠธ ์‹คํ–‰ ์‹œ ์ปดํฌ๋„ŒํŠธ์˜ ์ถœ๋ ฅ์ด ๋ณ€ํ•˜์ง€ ์•Š์•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.

์˜ˆ์ œ [4] - onClick ํ•จ์ˆ˜ ํ…Œ์ŠคํŠธ

Button ์ปดํฌ๋„ŒํŠธ์˜ onClick ํ•จ์ˆ˜๋ฅผ ํ…Œ์ŠคํŠธ ํ•˜๊ธฐ ์œ„ํ•œ ํ…Œ์ŠคํŠธ ๋ช…์„ธ์ด๋‹ค.
it('clicks the button', () => {
    โ‘  const handlerClick = jest.fn(); 
    โ‘ก render(<Button label="Button Test" onClick={handlerClick} />); 
    โ‘ข const label = screen.getByText('Button Test'); 
    โ‘ฃ expect(handleClick).toHaveBeenCalledTimes(0); 
    โ‘ค fireEvent.click(label); 
    โ‘ฅ expect(handlerClick).toHaveBeenCalledTimes(1); 
});
โ‘  ํด๋ฆญ ํ•ธ๋“ค๋Ÿฌ ํ•จ์ˆ˜๋กœ jest.fn()์„ ์‚ฌ์šฉํ•˜์—ฌ mock ํ•จ์ˆ˜ ์ƒ์„ฑ
โ‘ก Button ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•˜๊ณ , onClick props์— mock ํ•จ์ˆ˜ ์ „๋‹ฌ
โ‘ข ํ™”๋ฉด์—์„œ "Button Test" ๋ผ๋Š” ํ…์ŠคํŠธ๋ฅผ ๊ฐ€์ง„ ์š”์†Œ๋ฅผ ์ฐพ์Œ
โ‘ฃ ํด๋ฆญ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜๋Š”์ง€ ํ™•์ธ
โ‘ค ์ฐพ์€ ์š”์†Œ์— ํด๋ฆญ ์ด๋ฒคํŠธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ด
โ‘ฅ ํด๋ฆญ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๊ฐ€ ํ•œ ๋ฒˆ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€ ํ™•์ธ
๊ทธ๋‹ค์Œ ํ™”๋ฉด์— ํ‘œ์‹œ๋œ ๋ฒ„ํŠผ ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ์•„์„œ ์•„์ง ํ•ด๋‹น ์ปดํฌ๋„ŒํŠธ๋ฅผ ํด๋ฆญํ•˜์ง€ ์•Š์•˜์Œ์„ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด toHaveBeenCalledTimes ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์šฐ๋ฆฌ๊ฐ€ ๋งŒ๋“  ๋ชจ์˜ํ•จ์ˆ˜๊ฐ€ ์ž˜ ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•ด์ค€๋‹ค.

์˜ˆ์ œ [5] - <Input /> ์ž…๋ ฅ๋œ ๊ฐ’์ด ์ผ์น˜ ์ฒดํฌ

๊ด€๋ จ ํ‚ค์›Œ๋“œ

getByDisplayValue

getByPlaceholderText

fireEvent

fireEvent.change

๐Ÿ“ getByDisplayValue๋ฅผ ์ด์šฉํ•˜์—ฌ input์„ ์ฐพ์•„ ๋ Œ๋”๋ง ํ•˜๋Š” ์˜ˆ์ œ

import React from 'react';
import { render, screen, fireEvent } from 'testing-library/react';
import 'jest-styled-components';
import {Input} from './index';

describe('<Input />', ()=>{
	it('renders component correctly', ()=>{
      โ‘  const { container } = render(<Input value="default value" />);
      โ‘ก const label = screen.getByDisplayValue("default value");
      โ‘ข expect(label).toBeInTheDocument();
      โ‘ฃ expect(container).toMatchSnapshot();
   })
})
์ด ์ฝ”๋“œ๋Š” React ์ปดํฌ๋„ŒํŠธ ๋ฅผ ํ…Œ์ŠคํŠธํ•˜๊ธฐ ์œ„ํ•ด ์ž‘์„ฑ๋œ Jest ํ…Œ์ŠคํŠธ ํŒŒ์ผ์ด๋‹ค. react-testing-library์™€ jest-styled-components๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ปดํฌ๋„ŒํŠธ์˜ ๋ Œ๋”๋ง ๋ฐ ๋™์ž‘์„ ํ•œ๋‹ค.
โ‘  render ํ•จ์ˆ˜๋Š” ์ปดํฌ๋„ŒํŠธ๋ฅผ ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ value prop์œผ๋กœ "default value"๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
โ‘ก screen.getByDisplayValue ํ•จ์ˆ˜๋Š” ์ฃผ์–ด์ง„ ๊ฐ’("default value")์„ ํ‘œ์‹œํ•˜๋Š” ์š”์†Œ๋ฅผ ๊ฒ€์ƒ‰ํ•ฉ๋‹ˆ๋‹ค.
ใ€€์—ฌ๊ธฐ์„œ๋Š” ์ปดํฌ๋„ŒํŠธ๊ฐ€ "default value"๋ผ๋Š” ๊ฐ’์„ ๊ฐ€์ง„ ์ž…๋ ฅ ํ•„๋“œ๋ฅผ ๋ Œ๋”๋งํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
โ‘ข expect ํ•จ์ˆ˜๋Š” ๋‹จ์–ธ(assertion)์„ ์ •์˜ํ•ฉ๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ๋Š” label ์š”์†Œ๊ฐ€ ๋ฌธ์„œ ๋‚ด์— ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค. ใ€€toBeInTheDocument ๋งค์ฒ˜๋Š” ์š”์†Œ๊ฐ€ ์‹ค์ œ๋กœ DOM์— ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•ฉ๋‹ˆ๋‹ค.
Input ์ปดํฌ๋„ŒํŠธ๋Š” Button ์ปดํฌ๋„ŒํŠธ์™€ ๋‹ค๋ฅด๊ฒŒ ํ•„์ˆ˜ Props๊ฐ€ ์กด์žฌํ•˜์ง€ ์•Š๋Š”๋‹ค. ๋”ฐ๋ผ์„œ ํ™”๋ฉด์— ํ‘œ์‹œ๋˜์—ˆ๋Š”์ง€๋ฅผ ์•Œ๊ธฐ ์œ„ํ•ด ๊ฒ€์ƒ‰(Query)ํ•  ๋ฐฉ๋ฒ•์ด ์—†๋‹ค.
๊ทธ๋ž˜์„œ Input ์ปดํฌ๋„ŒํŠธ์˜ ํ•„์ˆ˜๊ฐ€ ์•„๋‹Œ Props์ธ value๊ฐ’์„ ์„ค์ •ํ•˜๊ณ  react-testing-library์˜ screen.getByDisplayValue๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ input ์ปดํฌ๋„ŒํŠธ๋ฅผ ์ฐพ๋Š”๋‹ค.

๐Ÿ“ getByPlaceholderText๋ฅผ ์ด์šฉํ•˜์—ฌ input์„ ์ฐพ์•„ ๋ Œ๋”๋ง ํ•˜๋Š” ์˜ˆ์ œ

it('renders placeholder correctly', ()=>{
    render(<Input placeholder="default placeholder" />);

    const input = screen.getByPlaceholderText("default placeholder");
    expect(input).toBeInTheDocument();        
})

๐Ÿ“ fireEvent๋ฅผ ์ด์šฉํ•˜์—ฌ ์ด๋ฒคํŠธ ํ…Œ์ŠคํŠธ ํ•˜๋Š” ์˜ˆ์ œ

import { render, screen, fireEvent } from 'testing-library/react';

it('renders placeholder correctly', ()=>{
    render(<Input placeholder="default placeholder" />);
        
    const input = screen.getByPlaceholderText("default placeholder") as HTMLInputElement;
        
    fireEvent.change(input, {target:{ value:'study react'}})
    expect(input.value).toBe('study react')
})
Input ์ปดํฌ๋„ŒํŠธ์˜ placeholder๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Input ์ปดํฌ๋„ŒํŠธ๋ฅผ ํ™”๋ฉด์— ํ‘œ์‹œํ•˜๊ณ  ํ•ด๋‹น์ปดํฌ๋„ŒํŠธ๋ฅผ getByPlaceholderText๋ฅผ ํ†ตํ•ด ์ฐพ์•˜๋‹ค. ์ด๋ ‡๊ฒŒ ์ฐพ์€ ์ปดํฌ๋„ŒํŠธ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ HTMLElementํƒ€์ž…์ด๋‹ค. ํ•˜์ง€๋งŒ HTML์˜ input ํƒœ๊ทธ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์œผ๋ฏ€๋กœ, ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์˜ as๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ HTMLInputElement๋กœ ํƒ€์ž…๋ณ€ํ™˜์„ ํ•ด์ค€๋‹ค.
fireEvent์˜ changeํ•จ์ˆ˜๋กœ ์‹ค์ œ ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ์ž…๋ ฅํ•˜๋Š” ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑ ํ•ด์ค€๋‹ค. ์•ž์—์„œ ์ฐพ์€ Input ์ปดํฌ๋„ŒํŠธ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ž…๋ ฅํ•ด์ฃผ๊ณ  ์ž…๋ ฅ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์‹ค์ œ๋กœ ํ™”๋ฉด์— ์ž˜ ํ‘œ์‹œ ๋˜๊ณ  ์žˆ๋Š”์ง€๋ฅผ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด toBe๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ input, value๊ฐ’์ด ์šฐ๋ฆฌ๊ฐ€ fireEvent๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ž…๋ ฅํ•œ ๊ฐ’๊ณผ ๊ฐ™์€์ง€ ํ™•์ธ ํ•˜์˜€๋‹ค.