베이직 250114
https://teamsparta.notion.site/9-Jest-e84e953b03a54bcaa31dd4309e3857b5
9주차 Jest - 테스트
테스트 : 코드가 의도대로 동작하는지 확인하는 과정.
오류가 없는지, 결과가 올바른지 미리 확인.
예시 - 토스의 채용공고
https://toss.im/career/job-detail?job_id=6309320003
합류하면 함께할 업무예요
사용자의 모바일 행동이 쉬워지도록, 다양한 금융 데이터를 자동화하는 업무를 하고 있어요.
숨은 환금급 찾기 및 인컴의 신사업에서 복잡한 도메인, 대량의 트래픽을 다루며 유저의 세금 혁신에 기여할 수 있어요.
내부 라이브러리를 공용으로 사용할 수 있도록 이전하고, 스크래핑 인프라를 세팅해요.
OOP, FP 기반의 좋은 코드 퀄리티와 높은 테스트 커버리지로, 기능 추가 및 유지 보수가 쉬운 프로젝트를 운영할 수 있어요.
테스트, 로깅, 배포 환경에서 비효율적이거나 개선할 수 있는 부분들을 마음껏 개선할 수 있어요
이력서는 이렇게 작성하시는 걸 추천해요
Node.js 기반의 서버 개발 경험을 작성해 주세요.
불편한 인터페이스를 자동화한 경험을 작성해 주세요.
JavaScript, TypeScript, Web Browser, HTTP 관련 프로젝트 경험을 작성해 주세요.
AWS, GitHub, GOCD 등의 경험이 있으시면 도움이 돼요. 관련 개발 경험이 있으시다면 기재해 주세요.
Functional Programming, Object Oriented Programming, Test Driven Development 경험이 있으시다면 기재해 주세요.
토스인컴에서 사용하는 기술
TypeScript, Node.js, Nest.js, ELK
Jest, ramda
yarn, pnpm
mikroORM
__________________________________________________
버그(오류) 사전 방지
유지보수
한 부분의 코드를 수정했을 때, 다른 부분에 영향을 주지 않는지 확인하기 쉬워져요. 그렇기에 팀 협업 시 서로의 변경 사항이 버그를 일으키는지 빠르게 체크가 가능하죠.
품질 향상
꾸준히 테스트를 돌리면, 코드의 품질이 자동으로 보증되기 쉬워지는데요. 이게 무슨 의미냐면 제가 변경한 코드가 다른 부분에 영향을 어떻게 주는지 확실하게 알 수 있기에 자신감을 가지고 코드를 수정, 추가할 수 있게 되는거죠.
1. Jest는 뭔데?
Jest : Node.js 환경에서 사용하는 테스트 프레임워크
다른 테스트 프레임워크(예: Mocha, Jasmine)보다
설정이 간편: 다른 라이브러리들에 비해 환경 설정이 쉽고, 빠르게 시작 가능
직관적인 API: test, expect, describe 등 함수/메서드 이름이 직관적이어서 쉬움
___________________________________________________________________________
jest 사용하기
우선 간단하게 폴더를 만들고 프로젝트를 시작해봅시다.
mkdir test && cd test
npm init -y
그리고 Jest를 설치해봅시다.
npm install --save-dev jest
jest와 우리가 사용하는 node의 버전 등을 맞춰주기 위해 아래 설정 파일도 추가해주세요.
// jest.config.js export default { transform: {}, moduleNameMapper: { "^(\\.{1,2}/.*)\\.js$": "$1", }, }; |
// package.json { "name": "basic", "version": "1.0.0", "type": "module", "main": "index.js", "scripts": { "test": "node --experimental-vm-modules node_modules/jest/bin/jest.js" }, "author": "", "license": "ISC", "description": "", "devDependencies": { "jest": "^29.7.0" } } |
화면에 보여줘라
_____________________________________________
사용
주황색이 테스트 파일
예시1) 덧샘 함수 테스트
// calculator.js export const add = (a, b) => a + b; |
// calculator.test.js import { add } from "./calculator.js"; test("두 수를 더하는 함수 테스트", () => { expect(add(1, 2)).toBe(3); }); |
> npm test > basic@1.0.0 test > node --experimental-vm-modules node_modules/jest/bin/jest.js PASS ./calculator.test.js ✓ 두 수를 더하는 함수 테스트 (1 ms) Test Suites: 1 passed, 1 total Tests: 1 passed, 1 total Snapshots: 0 total Time: 0.105 s Ran all test suites. |
실패 확인
// calculator.test.js import { add } from "./calculator.js"; test("두 수를 더하는 함수 테스트", () => { expect(add(1, 2)).toBe(4); }); |
> npm test > basic@1.0.0 test > node --experimental-vm-modules node_modules/jest/bin/jest.js FAIL ./calculator.test.js ✕ 두 수를 더하는 함수 테스트 (2 ms) ● 두 수를 더하는 함수 테스트 expect(received).toBe(expected) // Object.is equality Expected: 4 Received: 3 2 | 3 | test("두 수를 더하는 함수 테스트", () => { > 4 | expect(add(1, 2)).toBe(4); | ^ 5 | }); 6 | at Object.<anonymous> (calculator.test.js:4:21) Test Suites: 1 failed, 1 total Tests: 1 failed, 1 total Snapshots: 0 total Time: 0.111 s, estimated 1 s |
_________________
테스트 함수 살펴보기
// 원본 // test, expect, toBe 이렇게 3개의 jest 기능을 사용 import { add } from "./calculator.js"; test("두 수를 더하는 함수 테스트", () => { expect(add(1, 2)).toBe(4); }); |
// test: 실제 테스트를 수행하는 함수 // 여기 안에 테스트 로직을 작성해서 사용 // 더하기 함수가 제대로 동작하는지 테스트 라는 것은 테스트에 대한 설명 test('더하기 함수가 제대로 동작하는지 테스트', () => { const result = sum(1, 2); expect(result).toBe(3); }); |
// Expect: 테스트 대상 함수를 실행한 결과를 “예상” 값과 비교해 확인하는 기능 //보통 expect(결과값).toBe(예상값) 형태로 작성. //그 외에 toEqual, toContain, toBeTruthy 등 사용 가능 import { add } from "./calculator.js"; describe("calculator", () => { test("두 수를 더하는 함수 성공 테스트", () => { expect(add(1, 2)).toBe(3); }); test("두 수를 더하는 함수 실패 테스트", () => { expect(add(1, 2)).not.toBe(4); }); }); |
Describe: 여러 개의 테스트를 주제별로 묶고 싶을 때 사용. 유사한 테스트를 그룹화하면 관리하기 쉽고, 가독성도 좋아짐 |
배열은 저장된 주소가 달라서 동일하지 않음.
보이는 값이 동일한지 비교하려면 toequer 사용
참거짓 확인하기
보유여부 확인
특정 오류 발생여부 확인하기
여기서는 somthing brock
ect
beforeEach이치 : 모든 테스트를 실행하기 전에 실행
다른 기능
// expect.toBe
expect(3).toBe(3);
expect('hello').toBe('hello');
toEqual: 객체/배열의 내용이 같은지 확인
expect([1, 2, 3]).toEqual([1, 2, 3]);
expect({ a: 1, b: 2 }).toEqual({ a: 1, b: 2 });
toBeTruthy / toBeFalsy: true 또는 false 확인
expect(true).toBeTruthy();
expect(false).toBeFalsy();
expect(0).toBeFalsy(); // 0은 Falsy로 평가
expect('Hello').toBeTruthy(); // 빈 문자열이 아니면 Truthy
toContain: 배열이나 문자열에 특정 값이 포함되어 있는지
expect([1, 2, 3]).toContain(2);
expect('Hello World').toContain('World');
toThrow: 함수 호출 시 오류(예외)가 발생하는지, 해당 메시지가 맞는지 확인
expect(() => {
throw new Error('Something broke!');
}).toThrow('Something broke!');
__________________
복잡한 테스트
// 코드 // calculator.js export const add = (a, b) => { return a + b; }; export const subtract = (a, b) => { return a - b; }; export const multiply = (a, b) => { return a * b; }; export const divide = (a, b) => { if (b === 0) { throw new Error("Cannot divide by zero"); } return a / b; }; |
// calculator.test.js import { add, subtract, multiply, divide } from "./calculator"; describe("수학 함수 테스트", () => { beforeEach(() => { // 각 테스트 시작 전 실행되는 부분 // 보통 초기화 작업 등을 넣을 수 있음 }); afterEach(() => { // 각 테스트 끝난 후 실행되는 부분 }); test("1 + 2 = 3", () => { const result = add(1, 2); expect(result).toBe(3); // 3이 맞는지 확인 }); test("5 - 2 = 3", () => { const result = subtract(5, 2); expect(result).toBe(3); // 3이 맞는지 확인 }); test("3 * 4 = 12", () => { const result = multiply(3, 4); expect(result).toBe(12); }); test("10 ÷ 2 = 5", () => { const result = divide(10, 2); expect(result).toBe(5); }); test("0으로 나누기 시 에러가 발생하는지 테스트", () => { // divide(숫자, 0) 호출 시 에러를 던진다고 했으므로, toThrow로 확인 expect(() => { divide(5, 0); }).toThrow("Cannot divide by zero"); }); }); |
________________________________________________
목 레퍼지토리
임시 데이터 만들고 해당 함수만테스트
3Layered + Jest
이전 시간에 배웠던 3 Layered Architecture에 Jest는 어떻게 적용하면 될까요?
우선 간단한 예시를 만들어볼게요.
import express from "express";
import router from "./router.js";
const app = express();
app.use(router);
app.listen(3000, () => {
console.log("Server is running on port 3000");
});
// router.js
import express from "express";
import { Controller } from "./controller.js";
import { Service } from "./service.js";
import { Repository } from "./repository.js";
const router = express.Router();
const repository = new Repository([]);
const service = new Service(repository);
const controller = new Controller(service);
router.get("/", (req, res) => controller.findAll(req, res));
export default router;
// repository.js
export class Repository {
#data;
constructor(data = []) {
this.#data = data;
}
findAll() {
return this.#data;
}
}
// service.js
export class Service {
#repository;
constructor(repository) {
this.#repository = repository;
}
findAll() {
const data = this.#repository.findAll();
return data.map((item) => ({
...item,
name: item.name.toUpperCase(),
}));
}
}
// controller.js
export class Controller {
#service;
constructor(service) {
this.#service = service;
}
findAll(req, res) {
const data = this.#service.findAll();
res.json(data);
}
}
익숙한 3 Layered 아키텍쳐죠?
이걸 테스트로 만들어볼게요.
// repository.test.js
import { Repository } from "./repository.js";
describe("Repository", () => {
let repository;
const mockData = [
{ id: 1, name: "Item 1" },
{ id: 2, name: "Item 2" },
{ id: 3, name: "Item 3" },
];
beforeEach(() => {
repository = new Repository(mockData);
});
describe("findAll", () => {
it("should return all data", () => {
const result = repository.findAll();
expect(result).toEqual(mockData);
});
it("should return empty array when initialized without data", () => {
const emptyRepository = new Repository();
const result = emptyRepository.findAll();
expect(result).toEqual([]);
});
});
});
// service.test.js
import { jest } from "@jest/globals";
import { Service } from "./service.js";
describe("Service", () => {
let mockRepository;
let service;
beforeEach(() => {
// Mock repository with a findAll method
mockRepository = {
findAll: jest.fn(),
};
service = new Service(mockRepository);
});
describe("findAll", () => {
it("should return data with uppercase names", () => {
// Arrange
const mockData = [
{ id: 1, name: "john" },
{ id: 2, name: "jane" },
];
mockRepository.findAll.mockReturnValue(mockData);
// Act
const result = service.findAll();
// Assert
expect(result).toEqual([
{ id: 1, name: "JOHN" },
{ id: 2, name: "JANE" },
]);
expect(mockRepository.findAll).toHaveBeenCalledTimes(1);
});
it("should handle empty data array", () => {
// Arrange
mockRepository.findAll.mockReturnValue([]);
// Act
const result = service.findAll();
// Assert
expect(result).toEqual([]);
expect(mockRepository.findAll).toHaveBeenCalledTimes(1);
});
});
});
// controller.test.js
import { jest } from "@jest/globals";
import { Controller } from "./controller.js";
describe("Controller", () => {
let mockService;
let controller;
let mockRequest;
let mockResponse;
beforeEach(() => {
// Create mock service
mockService = {
findAll: jest.fn(),
};
// Create mock request and response
mockRequest = {};
mockResponse = {
json: jest.fn(),
};
// Initialize controller with mock service
controller = new Controller(mockService);
});
describe("findAll", () => {
test("should return data from service and send as JSON response", () => {
// Arrange
const mockData = [
{ id: 1, name: "Item 1" },
{ id: 2, name: "Item 2" },
];
mockService.findAll.mockReturnValue(mockData);
// Act
controller.findAll(mockRequest, mockResponse);
// Assert
expect(mockService.findAll).toHaveBeenCalledTimes(1);
expect(mockResponse.json).toHaveBeenCalledWith(mockData);
});
});
});
'내일배움캠프_게임서버(202410) > 분반 수업 Basic-A' 카테고리의 다른 글
베이직 250121 타입스크립트 (0) | 2025.01.21 |
---|---|
(수정중)코드 분리 - Layered Architecture Pattern (0) | 2025.01.07 |
(진행)7주차 - Acces Token / Refresh Token, API, Insomnia (0) | 2025.01.01 |
숙제하기 - 6주차 인증 (Session / Cookie / JWT) (0) | 2024.12.24 |
교육과정 틀기 (0) | 2024.12.19 |