単体テストは、コードの小さな部分をチェックして正しく動作することを確認し、バグを早期に発見するため、重要です。アプリをリリースする前にこれらのテストを行うことが重要です。このガイドでは、Mocha と Chai を使用した単体テストについて説明します。
Mocha は、Node.js 上で実行される機能が豊富な JavaScript テスト フレームワークで、非同期テストを簡単かつ楽しいものにします。特定の順序で実行し、テスト結果を収集し、正確なレポートを提供する機能を提供します。
Chai は、任意の JavaScript テスト フレームワークで使用できる BDD/TDD アサーション ライブラリです。いくつかのインターフェイスが提供されているため、開発者は最も使いやすいアサーション スタイルを選択できます。
読みやすく表現力豊かなアサーション
Chai は、Mocha と連携して機能するさまざまなアサーション スタイルと構文オプションを提供しており、明瞭さと読みやすさのニーズに合ったスタイルを選択できます。
非同期テストのサポート
Mocha は非同期テストを簡単に処理できるため、追加のライブラリや複雑なセットアップを必要とせずに、Node.js アプリケーションで非同期コードをテストできます。
まず、新しい Node.js プロジェクトをセットアップし、必要な依存関係をインストールしましょう。
mkdir auth-api-testing cd auth-api-testing npm init -y # Install production dependencies npm install express jsonwebtoken mongoose bcryptjs dotenv # Install development dependencies npm install --save-dev mocha chai chai-http supertest nyc
次のスクリプトを package.json に追加します:
{ "scripts": { "test": "NODE_ENV=test mocha --timeout 10000 --exit", "test:coverage": "nyc npm test" } }
テストに入る前に、テストするアプリケーションについて理解しましょう。私たちは、次の機能を備えた、シンプルだが安全な認証 API を構築しています。
src/ ├── models/ │ └── user.model.js # User schema and model ├── routes/ │ ├── auth.routes.js # Authentication routes │ └── user.routes.js # User management routes ├── middleware/ │ ├── auth.middleware.js # JWT verification middleware │ └── validate.js # Request validation middleware ├── controllers/ │ ├── auth.controller.js # Authentication logic │ └── user.controller.js # User management logic └── app.js # Express application setup
POST /api/auth/register - Registers new user - Accepts: { email, password, name } - Returns: { token, user } POST /api/auth/login - Authenticates existing user - Accepts: { email, password } - Returns: { token, user }
GET /api/users/profile - Gets current user profile - Requires: JWT Authentication - Returns: User object PUT /api/users/profile - Updates user profile - Requires: JWT Authentication - Accepts: { name, email } - Returns: Updated user object
テスト固有の構成用に .env.test ファイルを作成します:
PORT=3001 MONGODB_URI=mongodb://localhost:27017/auth-api-test JWT_SECRET=your-test-secret-key
最初のテスト ファイル test/auth.test.js を作成しましょう
const chai = require('chai'); const chaiHttp = require('chai-http'); const app = require('../src/app'); const User = require('../src/models/user.model'); chai.use(chaiHttp); const expect = chai.expect; describe('Auth API Tests', () => { // Runs before all tests before(async () => { await User.deleteMany({}); }); // Runs after each test afterEach(async () => { await User.deleteMany({}); }); // Test suites will go here });
Mocha は、テストのセットアップとクリーンアップのためのいくつかのフックを提供します。
before(): すべてのテストの前に 1 回実行します
after(): すべてのテスト後に 1 回実行します
beforeEach(): 各テストの前に実行します
afterEach(): 各テストの後に実行します
テストは、関連するテストをグループ化するための description() ブロックと、個々のテスト ケース用の it() ブロックを使用して編成されます。
mkdir auth-api-testing cd auth-api-testing npm init -y # Install production dependencies npm install express jsonwebtoken mongoose bcryptjs dotenv # Install development dependencies npm install --save-dev mocha chai chai-http supertest nyc
{ "scripts": { "test": "NODE_ENV=test mocha --timeout 10000 --exit", "test:coverage": "nyc npm test" } }
src/ ├── models/ │ └── user.model.js # User schema and model ├── routes/ │ ├── auth.routes.js # Authentication routes │ └── user.routes.js # User management routes ├── middleware/ │ ├── auth.middleware.js # JWT verification middleware │ └── validate.js # Request validation middleware ├── controllers/ │ ├── auth.controller.js # Authentication logic │ └── user.controller.js # User management logic └── app.js # Express application setup
POST /api/auth/register - Registers new user - Accepts: { email, password, name } - Returns: { token, user } POST /api/auth/login - Authenticates existing user - Accepts: { email, password } - Returns: { token, user }
GET /api/users/profile - Gets current user profile - Requires: JWT Authentication - Returns: User object PUT /api/users/profile - Updates user profile - Requires: JWT Authentication - Accepts: { name, email } - Returns: Updated user object
各テストは他のテストに依存せず、独立して実行する必要があります
テストは任意の順序で実行できる必要があります
適切なセットアップとクリーンアップには、before、after、beforeEach、afterEach フックを使用します
PORT=3001 MONGODB_URI=mongodb://localhost:27017/auth-api-test JWT_SECRET=your-test-secret-key
const chai = require('chai'); const chaiHttp = require('chai-http'); const app = require('../src/app'); const User = require('../src/models/user.model'); chai.use(chaiHttp); const expect = chai.expect; describe('Auth API Tests', () => { // Runs before all tests before(async () => { await User.deleteMany({}); }); // Runs after each test afterEach(async () => { await User.deleteMany({}); }); // Test suites will go here });
境界条件、エラー シナリオ、無効な入力、空または null 値をテストします。
describe('Auth API Tests', () => { describe('POST /api/auth/register', () => { it('should register a new user successfully', async () => { // Test implementation }); it('should return error when email already exists', async () => { // Test implementation }); }); });
テスト名は、テスト対象のシナリオを明確に説明し、一貫した命名規則に従い、予想される動作を含める必要があります。
describe('POST /api/auth/register', () => { it('should register a new user successfully', async () => { const res = await chai .request(app) .post('/api/auth/register') .send({ email: 'test@example.com', password: 'Password123!', name: 'Test User' }); expect(res).to.have.status(201); expect(res.body).to.have.property('token'); expect(res.body).to.have.property('user'); expect(res.body.user).to.have.property('email', 'test@example.com'); }); it('should return 400 when email already exists', async () => { // First create a user await chai .request(app) .post('/api/auth/register') .send({ email: 'test@example.com', password: 'Password123!', name: 'Test User' }); // Try to create another user with same email const res = await chai .request(app) .post('/api/auth/register') .send({ email: 'test@example.com', password: 'Password123!', name: 'Test User 2' }); expect(res).to.have.status(400); expect(res.body).to.have.property('error'); }); });
describe('Protected Routes', () => { let token; let userId; beforeEach(async () => { // Create a test user and get token const res = await chai .request(app) .post('/api/auth/register') .send({ email: 'test@example.com', password: 'Password123!', name: 'Test User' }); token = res.body.token; userId = res.body.user._id; }); it('should get user profile with valid token', async () => { const res = await chai .request(app) .get('/api/users/profile') .set('Authorization', `Bearer ${token}`); expect(res).to.have.status(200); expect(res.body).to.have.property('email', 'test@example.com'); }); it('should return 401 with invalid token', async () => { const res = await chai .request(app) .get('/api/users/profile') .set('Authorization', 'Bearer invalid-token'); expect(res).to.have.status(401); }); });
const mongoose = require('mongoose'); before(async () => { await mongoose.connect(process.env.MONGODB_URI_TEST); }); after(async () => { await mongoose.connection.dropDatabase(); await mongoose.connection.close(); });
describe('User CRUD Operations', () => { it('should update user profile', async () => { const res = await chai .request(app) .put(`/api/users/${userId}`) .set('Authorization', `Bearer ${token}`) .send({ name: 'Updated Name' }); expect(res).to.have.status(200); expect(res.body).to.have.property('name', 'Updated Name'); }); it('should delete user account', async () => { const res = await chai .request(app) .delete(`/api/users/${userId}`) .set('Authorization', `Bearer ${token}`); expect(res).to.have.status(200); // Verify user is deleted const user = await User.findById(userId); expect(user).to.be.null; }); });
チャイを使用したモカの手動テスト ケースを作成することは効果的ではありますが、多くの場合、いくつかの課題があります。
時間のかかる: 詳細なテスト スイートを手動で作成すると、特に大規模なコードベースの場合、かなりの時間がかかることがあります。
保守が難しい: アプリケーションが変更されると、手動テストの更新と保守がより複雑になり、エラーが発生しやすくなります。
一貫性のないカバレッジ: 開発者は、主要なパス、欠落しているエッジ ケース、または本番環境でバグを引き起こす可能性のあるエラー シナリオに重点を置く可能性があります。
スキル依存: 手動テストの品質と有効性は、開発者のテスト スキルとコードベースの精通度に大きく依存します。
反復的で退屈: 複数のコンポーネントまたは関数に対して同様のテスト構造を記述するのは退屈であり、詳細への注意力が低下する可能性があります。
フィードバックの遅延: 手動テストの作成に時間がかかると開発が遅れ、コードの品質と機能に関する重要なフィードバックが遅れる可能性があります。
これらの問題に取り組むために、Keploy は AI を使用してテスト プロセスを自動化および改善する ut-gen を導入しました。これは、コード セマンティクスを理解し、意味のある単体テストを作成する Meta LLM 研究論文の最初の実装です。
完全な単体テストを迅速に作成することで、単体テスト生成 (UTG) を自動化することを目的としています。これにより、反復的な手動作業の必要性が減り、手動で見逃しがちなより複雑なシナリオをカバーするためにテストを拡張することでエッジ ケースが改善され、コードベースの成長に合わせてテスト カバレッジが増加して完全なカバレッジが保証されます。
%[https://marketplace.visualstudio.com/items?itemName=Keploy.keployio]
単体テスト生成 (UTG) の自動化: 包括的な単体テストを迅速に生成し、余分な手動作業を削減します。
エッジケースの改善: 手動で見逃しがちなより複雑なシナリオをカバーするために、テストの範囲を拡張および改善します。
テスト カバレッジの向上: コードベースが拡大するにつれて、網羅的なカバレッジを確保することが可能になります。
結論として、アプリケーションの信頼性と強力さを求める開発者にとって、Mocha と Chai を使用した Node.js バックエンド テストをマスターすることは重要です。 Mocha のテスト フレームワークと Chai の明確なアサーション ライブラリを使用することで、開発者は、単純な単体テストから複雑な非同期操作に至るまで、コードの多くの部分をカバーする詳細なテスト スイートを作成できます。テストに焦点を当て続ける、明確な名前を使用する、Promise を正しく処理するなどのベスト プラクティスに従うと、テスト プロセスを大幅に改善できます。開発ワークフローでこれらのツールとテクニックを使用すると、バグを早期に発見し、コードの品質を向上させ、より安全で効率的なアプリケーションを提供できます。
どちらもテスト ツールですが、目的は異なります。 Mocha は、(describe() ブロックと it() ブロックを使用して) テストを編成および実行するための構造を提供するテスト フレームワークです。 Chai は、結果を検証するための関数 (expect()、 should、assert など) を提供するアサーション ライブラリです。 Chai なしで Mocha を使用することもできますが、これらを一緒に使用すると、より完全で表現力豊かなテスト ソリューションが得られます。
Mocha は、テスト データを管理するためのいくつかのライフサイクル フックを提供します。
before(): すべてのテストの前に 1 回実行します
beforeEach(): 各テストの前に実行
afterEach(): 各テストの後に実行します
after(): すべてのテスト後に 1 回実行します
例:
mkdir auth-api-testing cd auth-api-testing npm init -y # Install production dependencies npm install express jsonwebtoken mongoose bcryptjs dotenv # Install development dependencies npm install --save-dev mocha chai chai-http supertest nyc
describe() ブロックを使用して関連テストをグループ化する
予想される動作を明確に示すわかりやすいテスト名を使用します
各テストで AAA (Arrange-Act-Assert) パターンに従います
テストをアトミックかつ独立に保つ
ソース コード構造を反映するようにテスト ファイルを整理します
例:
{ "scripts": { "test": "NODE_ENV=test mocha --timeout 10000 --exit", "test:coverage": "nyc npm test" } }
「before」はすべてのテストの前に 1 回実行され、「beforeEach」は各個別のテストの前に実行され、「afterEach」は各個別のテストの後に実行され、「after」はすべてのテストが完了した後に 1 回実行されます。これらは、テスト データのセットアップとクリーンアップに役立ちます。
テストでは async/await または return Promise を使用できます。テスト関数の前に「async」を追加し、非同期操作を呼び出すときに「await」を使用するだけです。長時間の操作には適切なタイムアウトを設定してください。
以上がMocha と Chai を使用した NodeJS の単体テストの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。