or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

application-creation.mdcontext-session-mocking.mdenvironment-configuration.mdhttp-client-mocking.mdhttp-testing-utilities.mdindex.mdlogging-assertions.mdmock-restoration.mdservice-mocking.md
tile.json

service-mocking.mddocs/

Service Mocking

Method-level mocking for Egg.js services with support for custom implementations, error simulation, and both synchronous and asynchronous operations.

Capabilities

Service Method Mocking

Mock individual service methods with custom implementations or return values.

/**
 * Mock a service method
 * @param {string|Object} service - Service name (dot-notation) or service class
 * @param {string} methodName - Method name to mock
 * @param {Function|Object|Error} fn - Mock implementation or return value
 * @returns {Application} Application instance for chaining
 */
app.mockService(service, methodName, fn);

Usage Examples:

// Mock service method with return value
app.mockService('user', 'find', {
  id: 123,
  name: 'Alice',
  email: 'alice@example.com'
});

// Mock service method with function
app.mockService('user', 'create', (userData) => {
  return {
    id: Math.floor(Math.random() * 1000),
    ...userData,
    createdAt: new Date()
  };
});

// Mock nested service method
app.mockService('user.profile', 'getById', {
  userId: 123,
  avatar: 'avatar.jpg',
  bio: 'Software developer'
});

// Mock with async function
app.mockService('email', 'send', async (to, subject, body) => {
  await new Promise(resolve => setTimeout(resolve, 100));
  return { messageId: 'msg-123', status: 'sent' };
});

// Mock with generator function
app.mockService('data', 'process', function* (data) {
  yield { status: 'processing' };
  return { status: 'completed', result: data.length };
});

Service Error Mocking

Mock service methods to throw errors for testing error handling scenarios.

/**
 * Mock a service method to return an error
 * @param {string|Object} service - Service name (dot-notation) or service class
 * @param {string} methodName - Method name to mock
 * @param {Error|string} [err] - Error to throw (creates default if not provided)
 * @returns {Application} Application instance for chaining
 */
app.mockServiceError(service, methodName, err);

Usage Examples:

// Mock service to throw default error
app.mockServiceError('user', 'find');
// Throws: Error('mock find error')

// Mock service with custom error message
app.mockServiceError('user', 'create', 'User creation failed');

// Mock service with custom Error object
app.mockServiceError('user', 'delete', new Error('Permission denied'));

// Mock service with specific error type
app.mockServiceError('payment', 'charge', new TypeError('Invalid card number'));

// Mock service with error containing additional properties
const error = new Error('Database connection failed');
error.code = 'ECONNREFUSED';
error.statusCode = 503;
app.mockServiceError('database', 'query', error);

Service Class Mocking

Mock methods on service classes and prototype methods.

/**
 * Mock service using service class reference
 * @param {Object} service - Service class or prototype
 * @param {string} methodName - Method name to mock
 * @param {Function|Object|Error} fn - Mock implementation or return value
 */
app.mockService(serviceClass, methodName, fn);

Usage Examples:

// Mock using service class
const UserService = app.serviceClasses.user;
app.mockService(UserService, 'findById', { id: 123, name: 'Alice' });

// Mock prototype method
const UserService = app.serviceClasses.user;
app.mockService(UserService.prototype, 'validate', true);

// Mock nested service class
const ProfileService = app.serviceClasses.user.profile;
app.mockService(ProfileService, 'update', (id, data) => {
  return { id, ...data, updatedAt: new Date() };
});

Async and Sync Method Handling

Automatic handling of synchronous and asynchronous method mocking with proper return type conversion.

Usage Examples:

// Mock async method with sync return
app.mockService('user', 'findAsync', { id: 123 });
// Original method is async, but mock returns sync value
// egg-mock automatically wraps in Promise

// Mock sync method with async function
app.mockService('validator', 'checkSync', async (data) => {
  await new Promise(resolve => setTimeout(resolve, 10));
  return data.isValid;
});
// Original method is sync, but mock is async
// egg-mock handles the conversion

// Mock generator function
app.mockService('processor', 'runGenerator', function* () {
  yield 'step1';
  yield 'step2';
  return 'completed';
});

// Mock async generator function
app.mockService('stream', 'processAsync', async function* () {
  for (let i = 0; i < 3; i++) {
    await new Promise(resolve => setTimeout(resolve, 10));
    yield `item-${i}`;
  }
});

Multiple Service Mocking

Chain multiple service mocks and manage complex service interactions.

Usage Examples:

// Chain multiple service mocks
app.mockService('user', 'find', { id: 123, name: 'Alice' })
   .mockService('user', 'create', { id: 124, name: 'Bob' })
   .mockService('email', 'send', { status: 'sent' });

// Mock related services
app.mockService('user', 'findById', { 
  id: 123, 
  name: 'Alice',
  profileId: 456
});
app.mockService('profile', 'findById', {
  id: 456,
  userId: 123,
  avatar: 'alice.jpg'
});

// Mock service dependencies
app.mockService('payment', 'charge', (amount, cardToken) => {
  // Mock depends on other services
  return {
    id: 'charge-123',
    amount,
    status: 'succeeded',
    cardLast4: '4242'
  };
});
app.mockService('notification', 'send', async (userId, message) => {
  return { delivered: true, timestamp: Date.now() };
});

Service Mock Restoration

Individual service mock restoration and verification.

Usage Examples:

// Mock services for specific test
beforeEach(() => {
  app.mockService('user', 'find', { id: 123 });
  app.mockService('email', 'send', { sent: true });
});

// Restore all mocks after test
afterEach(() => {
  app.mockRestore();
});

// Verify service calls in test
it('should call user service', async () => {
  let findCalled = false;
  app.mockService('user', 'find', (id) => {
    findCalled = true;
    return { id, name: 'Alice' };
  });

  const user = await app.service.user.find(123);
  assert(findCalled);
  assert.equal(user.id, 123);
});

Service Mock with Context

Mock services that depend on context or request data.

Usage Examples:

// Mock service that uses context
app.mockService('auth', 'getCurrentUser', function() {
  // 'this' refers to the context in service methods
  return {
    id: this.userId || 123,
    name: 'Alice',
    role: this.userRole || 'user'
  };
});

// Mock context-dependent service
const ctx = app.mockContext({ userId: 456, userRole: 'admin' });
app.mockService('permission', 'check', function(resource) {
  return this.userRole === 'admin';
});

// Test context-dependent service
it('should respect context in service mock', async () => {
  const ctx = app.mockContext({ userId: 789 });
  app.mockService('audit', 'log', function(action) {
    return {
      userId: this.userId,
      action,
      timestamp: Date.now()
    };
  });

  const result = await ctx.service.audit.log('login');
  assert.equal(result.userId, 789);
});

Dynamic Service Mocking

Create dynamic service mocks that change behavior based on input or state.

Usage Examples:

// Dynamic return based on input
app.mockService('user', 'find', (id) => {
  if (id === 123) {
    return { id: 123, name: 'Alice', active: true };
  } else if (id === 124) {
    return { id: 124, name: 'Bob', active: false };
  } else {
    throw new Error('User not found');
  }
});

// Stateful service mock
let callCount = 0;
app.mockService('counter', 'increment', () => {
  return ++callCount;
});

// Mock with side effects
const emails = [];
app.mockService('email', 'send', (to, subject, body) => {
  emails.push({ to, subject, body, sentAt: Date.now() });
  return { messageId: `msg-${emails.length}` };
});

// Verify side effects
it('should track email sends', async () => {
  await app.service.email.send('test@example.com', 'Hello', 'World');
  assert.equal(emails.length, 1);
  assert.equal(emails[0].to, 'test@example.com');
});