프론트엔드 테스트 | Jest 기본 정리

쪼개발자·2023년 8월 16일

개인적으로 제일 재미없어 하는 테스트
프론트엔드 개발의 경우 테스트의 의미를 크게 못 느끼고 있는데, 테스트 좀 잘 하라고 그래서 강의를 들으면서 정리해본다^.^ 귀찮은걸 맨날 시키는게 회사지 뭐.

참조 강의 - Udemy: React Testing Library and Jest: The Complete Guide
https://www.udemy.com/course/react-testing-library-and-jest/learn/

Test 쉽게하기

screen.logTestingPlaygroundURL()

  • testing-playground를 통해 테스트를 쉽게 도와준다

npx rtl-book serve roles-notes.js


자주 사용하는 쿼리

  • getByRole
  • queryByRole
  • findByRole

그렇다면 언제 어떻게 사용하는지?

  • 요소가 있는지 확인할때는 getBy(1개), getAllBy(여러개)
const element = screen.getByRole('listitem');
expect(element).toBeInTheDocument();
  • 요소가 없는지 확인할때는 queryBy, queryAllBy
const element = screen.queryByRole('listitem');
expect(element).not.toBeInTheDocument();
  • 요소가 없는지 확인할때는 findBy, findAllBy
    공식문서: Make sure an element eventually exists
    무슨말이지? 라고 생각했는데 api를 통해 데이터를 가져오는 경우 사용하면 적합함
function fakeFetchColors() {
  return Promise.resolve(
      ['red', 'green', 'blue']
  );
}

function LoadColor() {
  const [colors, setColors] = useState([]);
  
  useEffect(() => {
      fakeFetchColors().then(c=> setColors(c));
  }, []);
  
  const renderedColors = colors.map(color => {
  	return <li key={color}>{color}</li>
  }
}

test('findBy or findAllBy when data fetching', async () => {
	render(<LoadColor />)
    // const els = screen.getAllByRole('listitem');
    // 에러! fetch해서 데이터를 가져오는 경우엔 에러가 남.
     const els = screen.findAllByRole('listitem');
    // 베리 굿!
    
    expect(els).toHaveLength(3);

요소를 찾기 힘들 때 자주 사용하는 방식

  1. data-testid
    const rows = within(screen.getByTestId('users')).getAllByRole('row')
    tr을 찾고 있음
  1. querySelector
    const {container} = render();
    const table = container.querySelector('table');
    const rows = container.querySelectorAll('tbody tr');

예제
1) textbox에 값을 넣고 button을 클릭하는 경우

render(<UserForm onUserAdd={() => {}} />
const nameInput = screen.getByRole('textbox', {name: /name/i} );
const emailInput = screen.getByRole('textbox', {name: /email/i });
const button = screen.getByRole('button');

user.click(nameInput);
user.keyboard('jane');
user.click(emailInput);
user.keyboard('jane@jane.com');
user.click(button);

2) element가 여러개인 경우에는 aria-label or name을 사용하면 편함. ex. button 여러개

<label>Email</label>
<input />
<label>Search</label>
<input />

//<button>sign in</button>
//<button>sign out</button>
<button aria-label="sign in"><svg /></button>
<button aria-label="sign out"><svg /></button>
test('버튼들이 있는지 확인', () => {
  render(<test />
  const signInButton = screen.getByRole('button', {
      name: /sign in/i // i는 대소문자 구별없이 찾기 위해
  });
  const signOutButton = screen.getByRole('button', {
      name: /sign out/i // i는 대소문자 구별없이 찾기 위해
  });
  expect(signInButton).toBeInTheDocument();
  expect(signOutButton).toBeInTheDocument();
});

3) getby, queryby, findby 비교: 없는 요소(ex.textbox)를 찾고자 하면?

test('없는 요소 찾으려고 할때 테스트',  async () => 
{
  render(<colorList />)
  expect(() => screen.getByRole('textbox')).toThrow(); 
  expect(() => screen.queryByRole('textbox')).toEqual(null); 
  // 만약 textbox가 두개 이상있는데 위 처럼 써주면 throw error임. toThrow로 써줘야함
  let errorThrown = false;
  try {
      await screen.findByRole('textbox');  
  } catch(err) {
    errorThrown = true;
  }
  expect(errorThrown).toEqual(true);
});

4) getby, queryby, findby 비교: 있는 요소(ex.textbox)를 찾고자 하면?

test('있는 요소 찾으려고 할때 테스트',  async () => 
{
  render(<colorList />)
  expect(screen.getByRole('list')).toBeInTheDocument();
  expect(screen.queryByRole('list')).toBeInTheDocument();
  expect(await screen.findByRole('list')).toBeInTheDocument();
});
  • findBy의 경우 async, await 사용하는 특징이 있음! 왜인지 모르는데 그냥 쓰자..

5) getAllBy, queryAllBy, findAllBy 비교

test('AllBy 테스트',  async () => 
{
  render(<colorList />)
  //listitem은 li를 의미하는 것
  expect(screen.getAllByRole('listitem')).toHaveLength(3); 
  expect(screen.queryAllByRole('list')).toHaveLength(3); 
  expect(await screen.findAllByRole('list')).toHaveLength(3); 
});

6) 다이나믹한 value 인 경우? RegExp사용하여 테스트

test('Displays information about the repository', () => {
  const repository = {
    language: 'Javascript',
    stargazers_count: 5,
    forks: 30,
    open_issues: 1
  }

  render(<RepositoriesSummary repository={repository} />);
  const language = screen.getByText('Javascript');
  const stars = screen.getByText(5);
  const forks = screen.getByText('30 Forks');
  const open_issues = screen.getByText('1 issues need help');
  expect(language).toBeInTheDocument();
  expect(stars).toBeInTheDocument();
  expect(forks).toBeInTheDocument();
  expect(open_issues).toBeInTheDocument();

  for (let key in repository) {
    const value = repository[key];
    const element = screen.getByText(new RegExp(value));
    expect(element).toBeInTheDocument();
  }

});

1개의 댓글

comment-user-thumbnail
2023년 8월 16일

좋은 글이네요. 공유해주셔서 감사합니다.

답글 달기