단위테스트(unit test) : 개별 컴포넌트나 함수를 테스트하는 것으로, 각 부분이 제대로 작동하는지 확인할 수 있다. 이 테스트는 설계와 구현 중에 버그를 찾는 데 가장 효과적이다.
통합테스트(integration test) : 여러 컴포넌트나 시스템을 함께 테스트하여 해당 부분들이 잘 협업하는지 확인한다. 다시말해서 시스템 전체가 잘 작동하는지 판단하기 위한 것이다.
E2E(end-to-end test) : 사용자 관점에서 애플리케이션을 테스트한다. 이것은 실제 사용자 경험을 가장 잘 시뮬레이션하며, 모든 계층 위에서 실행된다.
가장 작은 단위를 테스트 한다. 일반적으로 개별 컴포넌트나 함수의 동작을 검증한다.
Jest
를 사용하여 단위테스트를 작성한다.// MyComponent.js
import React from 'react';
const MyComponent = ({ text }) => {
return <div>{text}</div>;
};
export default MyComponent;
// MyComponent.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import MyComponent from './MyComponent';
test('renders text prop', () => {
render(<MyComponent text="Hello, World!" />);
const element = screen.getByText(/Hello, World!/i);
expect(element).toBeInTheDocument();
});
통합 테스트는 여러 컴포넌트나 모듈이 함께 동작하는 방식을 테스트 한다.
Jest
, React-testing-library
를 많이 사용한다.// App.js
import React from 'react';
import Header from './Header';
import Footer from './Footer';
const App = () => {
return (
<div>
<Header />
<main>Content goes here</main>
<Footer />
</div>
);
};
// App.test.js
import React from 'react';
import { render, screen } from '@testing-library/react';
import App from './App';
test('renders Header and Footer', () => {
render(<App />);
expect(screen.getByText(/Header/i)).toBeInTheDocument();
expect(screen.getByText(/Footer/i)).toBeInTheDocument();
});
export default App;
E2E 테스트는 애플리케이션의 전체 흐름을 테스트 한다. 사용자 관점에서 애플리케이션이 예상대로 동작하는지 검증한다.
Playwright
와 Cypress
를 사용하여 E2E 테스트를 작성한다.// e2e.spec.js
const { test, expect } = require('@playwright/test');
test('homepage has title', async ({ page }) => {
await page.goto('http://localhost:3000');
await expect(page).toHaveTitle(/React App/i);
});
// e2e.cy.js
describe('Homepage', () => {
it('should have the correct title', () => {
cy.visit('http://localhost:3000');
cy.title().should('include', 'React App');
});
});
단위 테스트는 개별 기능들이 모두 제대로 작동하는지 검증할 수 있어서 장애를 빨리 발견하고 수정할 수 있는 장점이 있다. 하지만, 모든 컴포넌트를 테스트해야한다는 점에서는 상당한 작업량과 시간이 소요된다.
통합 테스트는 개별적인 기능들이 아니라 서로 연결된 기능들이 원활하게 동작하는지 검증하기 때문에, 전체 시스템의 안정성을 평가할 수 있다는 장점이 있다. 그러나, 단위 테스트보다 문제를 파악하고 해결하는데 더 많은 시간이 소요될 수 있다.
E2E 테스트는 실질적인 사용자 경험을 가장 잘 반영하지만, 설정 자체가 복잡하고 실행 시간도 오래 걸리며, 실패한 경우 어디서 문제가 발생했는지 찾기 어렵다는 단점이 있다.
사내에서 주로 E2E 테스트를 많이 썼기 때문에 관련하여 어떻게 작성하면 좋을지 생각해 봤다.(Playwright)
최종 사용자가 페이지를 보거나, 상호작용이 가능한 페이지에 대해서 테스트를 수행한다.
테스트 각각에 대해서 격리해서 작업(local/session storage, data, cookies등)
테스트에 특정 반복을 피해야할 경우 before and after hooks를 이용
테스트 파일 내에서 특정 URL 이동, 앱의 이부에 로그인 진행 시 before hooks 이용
import { test } from '@playwright/test';
test.beforeEach(async ({ page }) => {
// Runs before each test and signs in each page.
await page.goto('https://github.com/login');
await page.getByLabel('Username or email address').fill('username');
await page.getByLabel('Password').fill('password');
await page.getByRole('button', { name: 'Sign in' }).click();
});
test('first', async ({ page }) => {
// page is signed in.
});
test('second', async ({ page }) => {
// page is signed in.
});
전체 앱 흐름을 테스트 할 경우 여러 작업에 대한 assertion 처리로 괜찮다.
긴 테스트를 굳이 개별 테스트로 나눌 경우 테스트 실행 속도를 늦추기 때문에 피해야 한다.
긴 테스트 에러 발생 시 종료하지 않고 에러만 표시할 경우 soft assertion 사용 가능하다.
// Make a few checks that will not stop the test when failed...
await expect.soft(page.getByTestId('status')).toHaveText('Success');
// ... and continue the test to check more things.
await page.getByRole('link', { name: 'next page' }).click();
await page.route('**/api/fetch_data_third_party_dependency', route =>
route.fulfill({
status: 200,
body: testData,
}),
);
await page.goto('https://example.com');
페이지의 특정 부분을 검색할 경우 chaning, filtering으로 좁히는 걸 추천
const product = page.getByRole('listitem').filter({ hasText: 'Product 2' });
await page
.getByRole('listitem')
.filter({ hasText: 'Product 2' })
.getByRole('button', { name: 'Add to cart' })
.click();
auto waiting, retry-ability 등의 기능을 사용하기 위해서 내장된 locator를 이용
page.getByRole('button', { name: 'submit' })
프로젝트의 크기와 중요성, 그리고 가능한 리소스 등을 고려하여 결정하는 것이 좋다. 만일 코드베이스가 작아 변경사항에 따른 영향력을 쉽게 파악할 수있다면, 단위 테스트를 진행하는 것이 좋을 수 있다. 반면에 프로젝트가 크고 복잡한 상호작용이 많다면, 통합 테스트나 E2E 테스트를 고려해보는 것도 좋다.