Jest는 페이스북에서 개발한 JavaScript 테스팅 프레임워크로, React application을 테스트할 때 많이 사용된다.
관련된 테스트 케이스를 그룹화하여 묶은 것. describe() 사용함.
단일 테스트. test() 또는 it()으로 작성함.
describe('테스트 스위트 이름', () => {
test('테스트 케이스 설명', () => {
// 테스트 코드
});
it('또 다른 테스트 케이스 설명', () => {
// 테스트 코드
});
});
function throwError() {
throw new Error('Error message');
}
expect(throwError).toThrow('Error message');
test('비동기 함수 테스트 with async/await', async () => {
const data = await fetchData();
expect(data).toBe('mocked data');
});
// 성공 테스트
test('프로미스가 성공적으로 해결됨', () => {
return expect(fetchData()).resolves.toBe('mocked data');
});
// 실패 테스트
test('프로미스가 오류를 반환', () => {
return expect(fetchError()).rejects.toThrow('error');
});
test('콜백 기반 비동기 코드 테스트', (done) => {
fetchData((data) => {
expect(data).toBe('mocked data');
done();
});
});
모든 테스트 케이스가 실행되기 전에 한 번 실행된다. 보통 환경 설정이나 초기화 작업을 할 때 사용한다.
ex) DB 연결 설정, 서버 시작
모든 테스트 케이스가 끝난 후에 한 번 실행된다. 보통 정리 작업을 할 때 사용한다.
ex) DB 연결 종료, 파일 닫기
각 테스트 케이스가 실행되기 전에 매번 실행된다. 테스트가 독립적으로 실행되도록 각 테스트 케이스에 필요한 초기 설정을 할 때 사용한다.
ex) 변수 초기화, DB 상태 재설정
각 테스트 케이스가 끝난 후 매번 실행된다. 테스트 후 정리 작업을 할 때 사용한다.
ex) 각 테스트 후 생성된 데이터 삭제
훅의 실행 순서 : beforeAll -> beforeEach -> test -> afterEach -> afterAll
describe('비동기 작업 테스트', () => {
let connection;
beforeAll(async () => {
connection = await connectToDatabase();
});
afterAll(async () => {
await connection.close();
});
beforeEach(async () => {
await connection.clearData();
});
test('비동기 데이터 처리', async () => {
await connection.addUser('John');
const user = await connection.getUser('John');
expect(user).toBeTruthy();
});
});
쿼리는 Testing Library에서 페이지의 요소를 찾는 데 제공하는 방법이다.
쿼리 : https://testing-library.com/docs/queries/about/#priority
'getByRole'의 role : https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role
jest.fn()은 가짜 함수 생성하는 함수이다. 이 함수는 호출 횟수나 전달된 인수 등을 추적할 수 있다.
const mockFn = jest.fn();
// 함수 호출
mockFn('first call');
mockFn('second call');
// 호출 횟수 및 인수 추적
expect(mockFn).toHaveBeenCalled();
expect(mockFn).toHaveBeenCalledTimes(2);
expect(mockFn).toHaveBeenCalledWith('first call');
mock 함수에 반환값을 설정할 수도 있다.
// 모든 호출에서 동일한 값 반환
const mockFn = jest.fn().mockReturnValue('default value');
expect(mockFn()).toBe('default value');
// 한 번만 특정 값을 반환하도록 설정
const mockFnOnce = jest.fn()
.mockReturnValueOnce('first call')
.mockReturnValueOnce('second call');
expect(mockFnOnce()).toBe('first call');
expect(mockFnOnce()).toBe('second call');
fn함수는 개별적으로 하나하나씩 모킹 처리한다면, mock 함수는 그룹을 한꺼번에 모킹처리한다. 즉, 테스트 환경에서 특정 모듈을 모킹할 수 있도록 지원한다. 이 함수는 모듈이나 의존성을 가짜로 만들어서 실제 모듈을 호출하는 대신 테스트에서만 사용하는 가짜 모듈을 대신 사용할 수 있게 한다.
// 외부 API를 사용하는 모듈을 mock하여 실제 API 호출을 방지하고, 가짜 데이터를 반환하도록 한다.
// userService.js
export const getUser = () => {
return fetch('/user').then(response => response.json());
};
// test.js
import { getUser } from './userService';
jest.mock('./userService', () => ({
getUser: jest.fn().mockResolvedValue({ id: 1, name: 'John' })
}));
test('mocking getUser API', async () => {
const user = await getUser();
expect(user).toEqual({ id: 1, name: 'John' });
});
Unit Test는 소프트웨어의 가장 작은 단위를 독립적으로 테스트하는 과정이다. 작은 코드 조각이 올바르게 동작하는지 확인할 수 있다.
// 어떤 함수 add(a, b)가 두 숫자를 더해 제대로 반환하는지 테스트
test('adds 1 + 2 to equal 3', () => {
expect(add(1, 2)).toBe(3);
});
통합 테스트는 여러 유닛이 함께 작동하는 방식을 테스트해서 유닛 간의 상호작용을 테스트한다.
여러 모듈이나 클래스 간의 상호작용을 포함하고, 데이터베이스, API, 파일 시스템 등과의 상호작용을 포함하기도 한다.
// 두 개의 모듈 userService와 authService가 잘 협력해서 사용자 인증을 처리하는지 확인
test('authenticates a user through userService and authService', () => {
const user = userService.getUser(1);
const isAuthenticated = authService.authenticate(user);
expect(isAuthenticated).toBe(true);
});
기능 테스트는 소프트웨어의 특정 기능을 테스트한다.
// 로그인 페이지에서 올바른 사용자 정보를 입력하면 시스템이 대시보드로 리디렉션 되는지 확인
test('allows a user to log in and redirects to dashboard', () => {
const loginPage = new LoginPage();
loginPage.enterCredentials('username', 'password');
loginPage.submit();
expect(browser.getCurrentUrl()).toBe('/dashboard');
});
애플리케이션의 전체 흐름을 실제 사용자의 시나리오처럼 테스트하여 시스템이 처음부터 끝까지 문제 없이 동작하는지 확인한다. 브라우저 상에서 user interaction을 시뮬레이션하여 프론트엔드, 백엔드, 데이터베이스를 전부 테스트한다.
// 사용자가 로그인한 후 대시보드에서 제품을 검색하고 장바구니에 넣고 결제하는 전체 과정을 시뮬레이션
describe('E2E - Shopping Cart Flow', () => {
it('should allow a user to search for a product, add it to the cart, and checkout', () => {
cy.visit('/login');
cy.get('input[name="username"]').type('user1');
cy.get('input[name="password"]').type('password123');
cy.get('button[type="submit"]').click();
cy.url().should('include', '/dashboard');
cy.get('input[name="search"]').type('laptop');
cy.get('button[type="submit"]').click();
cy.get('.product-list').contains('Laptop').click();
cy.get('.add-to-cart').click();
cy.get('.checkout').click();
cy.url().should('include', '/order-confirmation');
});
});