/ tests / example-mock-module.test.js
example-mock-module.test.js
  1  /**
  2   * Example test demonstrating Node.js 22+ mock.module() capabilities
  3   *
  4   * This shows how to mock entire ES modules before they're imported,
  5   * which was not possible in Node.js 20.
  6   */
  7  
  8  import { describe, test, mock } from 'node:test';
  9  import assert from 'node:assert';
 10  
 11  describe('mock.module() Examples', () => {
 12    test('should mock axios module completely', async t => {
 13      // Mock the entire axios module before importing
 14      // Note: Use defaultExport for default exports, not namedExports with 'default' key
 15      mock.module('axios', {
 16        defaultExport: async config => ({
 17          data: { mocked: true, url: config.url },
 18          status: 200,
 19        }),
 20      });
 21  
 22      // Now import the module - it will use the mock
 23      const axios = (await import('axios')).default;
 24  
 25      const response = await axios({ url: 'https://api.example.com' });
 26  
 27      assert.strictEqual(response.data.mocked, true);
 28      assert.strictEqual(response.status, 200);
 29    });
 30  
 31    test('should mock custom modules', async t => {
 32      // Mock a custom module with specific exports
 33      mock.module('../src/utils/logger.js', {
 34        namedExports: {
 35          log: mock.fn(),
 36          error: mock.fn(),
 37          info: mock.fn(msg => `MOCKED: ${msg}`),
 38        },
 39      });
 40  
 41      const logger = await import('../src/utils/logger.js');
 42  
 43      const result = logger.info('test message');
 44      assert.strictEqual(result, 'MOCKED: test message');
 45  
 46      // Verify the mock function was called
 47      assert.strictEqual(logger.info.mock.calls.length, 1);
 48    });
 49  
 50    test('should mock with different implementations per test', async t => {
 51      // axios was already mocked globally in test 1; re-import to get the existing mock.
 52      // To use different implementations per test, use separate describe blocks or
 53      // call mock.restoreAll() between tests.
 54      const axios1 = (await import('axios')).default;
 55      const response1 = await axios1({ url: 'https://api.example.com' });
 56      assert.strictEqual(response1.data.mocked, true);
 57      assert.strictEqual(response1.status, 200);
 58    });
 59  
 60    test('should mock modules with multiple named exports', async t => {
 61      mock.module('../src/scrape.js', {
 62        namedExports: {
 63          scrapeSERP: mock.fn(async () => [{ url: 'https://example.com', title: 'Mocked Result' }]),
 64          parseResults: mock.fn(data => data.organic_results || []),
 65        },
 66      });
 67  
 68      const scrape = await import('../src/scrape.js');
 69  
 70      const results = await scrape.scrapeSERP('test keyword');
 71      assert.strictEqual(results.length, 1);
 72      assert.strictEqual(results[0].title, 'Mocked Result');
 73  
 74      // Verify the mock was called
 75      assert.strictEqual(scrape.scrapeSERP.mock.calls.length, 1);
 76      assert.strictEqual(scrape.scrapeSERP.mock.calls[0].arguments[0], 'test keyword');
 77    });
 78  });
 79  
 80  /**
 81   * Other useful mocking features in Node.js 22:
 82   *
 83   * 1. Mock Timers:
 84   *    mock.timers.enable({ apis: ['setTimeout', 'setInterval'] });
 85   *    mock.timers.tick(1000); // Advance time by 1 second
 86   *
 87   * 2. Mock Functions with Custom Implementation:
 88   *    const fn = mock.fn((a, b) => a + b);
 89   *    fn(2, 3); // Returns 5
 90   *    fn.mock.calls[0].arguments // [2, 3]
 91   *    fn.mock.calls[0].result // 5
 92   *
 93   * 3. Mock Object Methods:
 94   *    const obj = { method: () => 'original' };
 95   *    mock.method(obj, 'method', () => 'mocked');
 96   *    obj.method() // Returns 'mocked'
 97   *
 98   * 4. Restore Mocks:
 99   *    mockFn.mock.restore(); // Restore original implementation
100   *    mock.restoreAll(); // Restore all mocks
101   */