방명록
- Node.js + MySQL 기반 REST API 단위 테스트 실습 튜터리얼2025년 07월 26일 23시 27분 22초에 업로드 된 글입니다.작성자: DandyNow728x90반응형
Node.js + MySQL 기반 REST API 단위 테스트 실습 튜터리얼
1. 단위 테스트란?
- 단위 테스트(Unit Test)는 애플리케이션의 가장 작은 단위(함수, 메서드 등)가 기대한 대로 동작하는지 독립적으로 검증하는 테스트이다.
- 실제 DB, 네트워크 등 외부 시스템에 의존하지 않고, 함수의 로직만 집중적으로 테스트한다.
2. 프로젝트 구조 예시
myapp/ ├── controller/ │ └── products.js ├── models/ │ └── Product.js ├── db.js ├── package.json └── test/ └── unit/ └── products.test.js
3. MySQL 데이터베이스 준비
- MySQL에서 테스트용 데이터베이스와 테이블을 생성한다.
CREATE DATABASE testdb; USE testdb; CREATE TABLE products ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, description TEXT NOT NULL );
4. Node.js 코드 작성
4-1. MySQL 연결 설정 (
db.js)// db.js const mysql = require("mysql2/promise"); const pool = mysql.createPool({ host: "localhost", user: "root", password: "비밀번호", // 본인 환경에 맞게 수정 database: "testdb", waitForConnections: true, connectionLimit: 10, queueLimit: 0, }); module.exports = pool;
4-2. Product 모델 (
models/Product.js)// models/Product.js const db = require("../db"); exports.create = async (product) => { const [result] = await db.query( "INSERT INTO products (name, description) VALUES (?, ?)", [product.name, product.description] ); return { id: result.insertId, ...product }; }; exports.findAll = async () => { const [rows] = await db.query("SELECT * FROM products"); return rows; }; exports.findById = async (id) => { const [rows] = await db.query("SELECT * FROM products WHERE id = ?", [id]); return rows[0]; }; exports.update = async (id, product) => { await db.query("UPDATE products SET name = ?, description = ? WHERE id = ?", [ product.name, product.description, id, ]); return { id, ...product }; }; exports.delete = async (id) => { await db.query("DELETE FROM products WHERE id = ?", [id]); return { id }; };
4-3. Product 컨트롤러 (
controller/products.js)// controller/products.js const ProductModel = require("../models/Product"); exports.createProduct = async (req, res, next) => { try { const product = await ProductModel.create(req.body); res.status(201).json(product); } catch (err) { next(err); } }; exports.getProducts = async (req, res, next) => { try { const products = await ProductModel.findAll(); res.status(200).json(products); } catch (err) { next(err); } }; exports.getProductById = async (req, res, next) => { try { const product = await ProductModel.findById(req.params.id); if (!product) { res.status(404).json({ message: "Not found" }); return; } res.status(200).json(product); } catch (err) { next(err); } }; exports.updateProduct = async (req, res, next) => { try { const product = await ProductModel.update(req.params.id, req.body); res.status(200).json(product); } catch (err) { next(err); } }; exports.deleteProduct = async (req, res, next) => { try { await ProductModel.delete(req.params.id); res.status(200).json({ message: "Deleted" }); } catch (err) { next(err); } };
5. 단위 테스트 코드 작성 (
test/unit/products.test.js)5-1. 필요한 라이브러리 설치
npm install --save-dev jest node-mocks-http5-2. 단위 테스트 코드
// test/unit/products.test.js const productController = require("../../controller/products"); const productModel = require("../../models/Product"); const httpMocks = require("node-mocks-http"); // 모델 함수 mock 처리 productModel.create = jest.fn(); productModel.findAll = jest.fn(); productModel.findById = jest.fn(); productModel.update = jest.fn(); productModel.delete = jest.fn(); let req, res, next; beforeEach(() => { req = httpMocks.createRequest(); res = httpMocks.createResponse(); next = jest.fn(); }); describe("Product Controller - Create", () => { it("should call ProductModel.create", async () => { req.body = { name: "테스트", description: "설명" }; await productController.createProduct(req, res, next); expect(productModel.create).toHaveBeenCalledWith(req.body); }); it("should return 201 and product data", async () => { const newProduct = { id: 1, name: "테스트", description: "설명" }; productModel.create.mockResolvedValue(newProduct); req.body = { name: "테스트", description: "설명" }; await productController.createProduct(req, res, next); expect(res.statusCode).toBe(201); expect(res._getJSONData()).toStrictEqual(newProduct); }); it("should handle errors", async () => { const error = new Error("DB Error"); productModel.create.mockRejectedValue(error); req.body = { name: "테스트", description: "설명" }; await productController.createProduct(req, res, next); expect(next).toHaveBeenCalledWith(error); }); }); describe("Product Controller - Get All", () => { it("should call ProductModel.findAll", async () => { await productController.getProducts(req, res, next); expect(productModel.findAll).toHaveBeenCalled(); }); it("should return 200 and products array", async () => { const products = [ { id: 1, name: "A", description: "descA" }, { id: 2, name: "B", description: "descB" }, ]; productModel.findAll.mockResolvedValue(products); await productController.getProducts(req, res, next); expect(res.statusCode).toBe(200); expect(res._getJSONData()).toStrictEqual(products); }); it("should handle errors", async () => { const error = new Error("DB Error"); productModel.findAll.mockRejectedValue(error); await productController.getProducts(req, res, next); expect(next).toHaveBeenCalledWith(error); }); }); describe("Product Controller - Get By Id", () => { it("should call ProductModel.findById", async () => { req.params.id = 1; await productController.getProductById(req, res, next); expect(productModel.findById).toHaveBeenCalledWith(1); }); it("should return 200 and product data", async () => { const product = { id: 1, name: "A", description: "descA" }; productModel.findById.mockResolvedValue(product); req.params.id = 1; await productController.getProductById(req, res, next); expect(res.statusCode).toBe(200); expect(res._getJSONData()).toStrictEqual(product); }); it("should return 404 if not found", async () => { productModel.findById.mockResolvedValue(undefined); req.params.id = 999; await productController.getProductById(req, res, next); expect(res.statusCode).toBe(404); expect(res._getJSONData()).toStrictEqual({ message: "Not found" }); }); it("should handle errors", async () => { const error = new Error("DB Error"); productModel.findById.mockRejectedValue(error); req.params.id = 1; await productController.getProductById(req, res, next); expect(next).toHaveBeenCalledWith(error); }); }); describe("Product Controller - Update", () => { it("should call ProductModel.update", async () => { req.params.id = 1; req.body = { name: "수정", description: "수정설명" }; await productController.updateProduct(req, res, next); expect(productModel.update).toHaveBeenCalledWith(1, req.body); }); it("should return 200 and updated product", async () => { const updated = { id: 1, name: "수정", description: "수정설명" }; productModel.update.mockResolvedValue(updated); req.params.id = 1; req.body = { name: "수정", description: "수정설명" }; await productController.updateProduct(req, res, next); expect(res.statusCode).toBe(200); expect(res._getJSONData()).toStrictEqual(updated); }); it("should handle errors", async () => { const error = new Error("DB Error"); productModel.update.mockRejectedValue(error); req.params.id = 1; req.body = { name: "수정", description: "수정설명" }; await productController.updateProduct(req, res, next); expect(next).toHaveBeenCalledWith(error); }); }); describe("Product Controller - Delete", () => { it("should call ProductModel.delete", async () => { req.params.id = 1; await productController.deleteProduct(req, res, next); expect(productModel.delete).toHaveBeenCalledWith(1); }); it("should return 200 and message", async () => { productModel.delete.mockResolvedValue({ id: 1 }); req.params.id = 1; await productController.deleteProduct(req, res, next); expect(res.statusCode).toBe(200); expect(res._getJSONData()).toStrictEqual({ message: "Deleted" }); }); it("should handle errors", async () => { const error = new Error("DB Error"); productModel.delete.mockRejectedValue(error); req.params.id = 1; await productController.deleteProduct(req, res, next); expect(next).toHaveBeenCalledWith(error); }); });
6. 실행 방법
- MySQL에서 DB와 테이블을 생성한다.
- Node.js 프로젝트에 위 파일들을 생성한다.
npm install --save-dev jest node-mocks-http로 테스트 환경을 구축한다.package.json에 아래와 같이 jest 스크립트를 추가한다.
"scripts": { "test": "jest" }- 아래 명령어로 테스트를 실행한다.
npm test
7. 마치며
- 이 튜터리얼은 MySQL DB를 사용하는 Node.js API의 단위 테스트를 처음부터 끝까지 실습할 수 있도록 구성하였다.
- 단위 테스트에서는 실제 DB에 접근하지 않고, 모델 함수만 mock 처리하여 컨트롤러 로직을 독립적으로 검증한다.
- 이 방식을 익히면, 다양한 DB 환경에서도 안정적이고 빠른 단위 테스트를 작성할 수 있다.
VSCode Extention
Jest Runner(firsttris)를 이용하면 편리한 테스트 환경을 구성할 수 있다.728x90반응형'언어·프레임워크 > Node.js' 카테고리의 다른 글
웹 애플리케이션 유저 삭제: 물리적 삭제와 논리적 삭제 (1) 2025.08.26 [Node.js] npm과 npx의 차이점과 사용법 (1) 2025.08.08 Node.js + MySQL 기반 REST API 통합 테스트 완벽 튜터리얼 (0) 2025.07.26 [Node.js] Jest 경로 전쟁: 복잡한 설정 대신 구조로 해결하기 (2) 2025.07.25 사용자 경험을 해치지 않는 봇 방어: 클라우드플레어 턴스타일(Cloudflare Turnstile)의 이해와 활용 (0) 2025.07.05 다음글이 없습니다.이전글이 없습니다.댓글