NodeJS环境下Mock ArangoJS单元测试及ArangoDB许可证查询函数问题
Got it, let's break down how to set up unit tests with mocking for your ArangoJS-powered listLicenses function in Node.js. I'll use Jest here since it's the most common testing framework with built-in mocking tools, but the core ideas apply to other tools like Sinon too.
背景
You're working with a service that uses ArangoDB to store license data, and you want to test the listLicenses function without spinning up a real database instance. The goal is to mock the ArangoJS client interactions so we can validate the function's logic in isolation.
核心思路
We need to:
- Mock the ArangoJS database connection and collection references
- Simulate the AQL query execution to return preset test data (or errors for failure cases)
- Verify that
listLicensescalls the right ArangoJS methods and returns the expected results
具体实现
First, let's assume your service code lives in licenseService.js (I've filled out the incomplete listLicenses function for context):
// licenseService.js const arangojs = require('arangojs'); class LicenseService { constructor() { this.db = new arangojs.Database({ url: 'http://localhost:8529' }); } async useLicenseDb() { // Switch to your license database await this.db.useDatabase('licenseDb'); // Authenticate if needed (mock this too!) await this.db.login('username', 'password'); } async listLicenses() { let result = []; await this.useLicenseDb(); const licenseCol = await this.db.collection('licenseCollection'); try { const query = ` FOR doc in licenseCollection RETURN { licenseId: doc.licenseId, licenseName: doc.licenseName, clientName: doc.clientName, useCase: doc.useCase, expirationDate: doc.expirationDate, callType: doc.callType } `; const cursor = await this.db.query(query); result = await cursor.all(); } catch (err) { console.error('Failed to fetch licenses:', err); throw err; } return result; } } module.exports = LicenseService;
Now, create your test file licenseService.test.js:
// licenseService.test.js const LicenseService = require('./licenseService'); const arangojs = require('arangojs'); // Mock the entire arangojs module to avoid real DB calls jest.mock('arangojs', () => { // Mock the cursor object that returns our test data const mockCursor = { all: jest.fn().mockResolvedValue([ { licenseId: 'LIC-001', licenseName: 'Enterprise Pro', clientName: 'Acme Corp', useCase: 'SaaS Platform', expirationDate: '2025-12-31', callType: 'REST' }, { licenseId: 'LIC-002', licenseName: 'Basic', clientName: 'Small Biz Inc', useCase: 'Internal Tools', expirationDate: '2024-06-30', callType: 'GraphQL' } ]) }; // Mock the database instance with required methods const mockDb = { useDatabase: jest.fn().mockResolvedValue(true), login: jest.fn().mockResolvedValue(true), collection: jest.fn().mockResolvedValue({}), query: jest.fn().mockResolvedValue(mockCursor) }; // Return the mock Database constructor return { Database: jest.fn().mockReturnValue(mockDb) }; }); describe('LicenseService.listLicenses', () => { let licenseService; // Reset mocks before each test to avoid cross-test contamination beforeEach(() => { licenseService = new LicenseService(); jest.clearAllMocks(); }); it('should fetch and return the correct list of licenses', async () => { // Execute the function const licenses = await licenseService.listLicenses(); // Verify ArangoJS methods were called correctly expect(arangojs.Database().useDatabase).toHaveBeenCalledWith('licenseDb'); expect(arangojs.Database().collection).toHaveBeenCalledWith('licenseCollection'); expect(arangojs.Database().query).toHaveBeenCalledWith(expect.stringContaining( 'FOR doc in licenseCollection RETURN {licenseId: doc.licenseId, licenseName: doc.licenseName, clientName: doc.clientName, useCase: doc.useCase, expirationDate: doc.expirationDate, callType: doc.callType}' )); // Verify the returned data matches our mock expect(licenses).toEqual([ { licenseId: 'LIC-001', licenseName: 'Enterprise Pro', clientName: 'Acme Corp', useCase: 'SaaS Platform', expirationDate: '2025-12-31', callType: 'REST' }, { licenseId: 'LIC-002', licenseName: 'Basic', clientName: 'Small Biz Inc', useCase: 'Internal Tools', expirationDate: '2024-06-30', callType: 'GraphQL' } ]); }); it('should handle query errors properly', async () => { // Override the mock query to throw an error const testError = new Error('AQL query failed: invalid syntax'); arangojs.Database().query.mockRejectedValue(testError); // Spy on console.error to verify it's called const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); // Verify the function throws the error await expect(licenseService.listLicenses()).rejects.toThrow(testError); // Verify the error was logged expect(consoleErrorSpy).toHaveBeenCalledWith('Failed to fetch licenses:', testError); // Clean up the spy consoleErrorSpy.mockRestore(); }); });
关键注意事项
- Mock Scope: Make sure your mock covers all ArangoJS methods your service uses (like
loginif you authenticate,useDatabase, etc.). - Test Coverage: Always test both success and failure paths to ensure error handling works as expected.
- Mock Cleanup: Use
jest.clearAllMocks()inbeforeEachto reset mock call history between tests. - Flexible Mocks: If you need different test data for different cases, you can override the mock's resolved value in individual tests (like we did for the error case).
依赖安装
Don't forget to install the required testing dependencies:
npm install --save-dev jest
内容的提问来源于stack exchange,提问作者Lëmön

