You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Jest测试文件下载请求?单元测试断言失败问题排查

Why Your Jest Test for downloadCdn Returns Null Response (and How to Fix It)

Let's break down why your test is failing and walk through the fixes step by step.

What's Going Wrong

Your test is failing for a few key reasons:

  • You're using undefined variables cdnUrl and out in your test case, so the function isn't receiving valid inputs.
  • The mock output stream (out) isn't set up to trigger the close event—this is the event that resolves the Promise in your downloadCdn function, so without it, the Promise never resolves properly.
  • Your request mock is too simplistic: it doesn't track event listeners or let you simulate the actual request lifecycle events like response, data, or end.

Fix Steps

  1. Create a mock output stream that tracks event listeners and lets you manually trigger the close event (critical for resolving the Promise).
  2. Enhance the request mock to handle event subscriptions and allow triggering events to replicate real download behavior.
  3. Define test values for cdnUrl and out instead of using undefined variables.
  4. Simulate the full request flow by triggering the necessary events in your test.

Fixed Test Code

Here's the updated test file that addresses all these issues:

import request from 'request';
import * as Module from './downloadCdn';

jest.mock('request', () => {
  // Create a mock request that tracks event listeners and supports triggering events
  const createMockRequest = () => {
    const eventListeners = {};
    return {
      pipe: jest.fn(() => mockOut), // Mimic real pipe behavior by returning the output stream
      on: jest.fn((event, callback) => {
        if (!eventListeners[event]) eventListeners[event] = [];
        eventListeners[event].push(callback);
      }),
      triggerEvent: (event, data) => {
        eventListeners[event]?.forEach(callback => callback(data));
      }
    };
  };

  return jest.fn(createMockRequest);
});

describe('Downloading a file', () => {
  let mockOut;
  let mockRequest;
  const testCdnUrl = 'http://localhost/test.zip'; // Define a test URL

  beforeEach(() => {
    // Reset mocks and create fresh test objects before each test
    request.mockClear();
    
    // Build a mock output stream with event handling
    const outEventListeners = {};
    mockOut = {
      on: jest.fn((event, callback) => {
        if (!outEventListeners[event]) outEventListeners[event] = [];
        outEventListeners[event].push(callback);
      }),
      triggerClose: () => {
        outEventListeners.close?.forEach(callback => callback());
      }
    };

    // Get the instance of our mock request
    mockRequest = request();
  });

  it('Should recognize downloadCdn as a function', () => {
    expect(typeof Module.downloadCdn === 'function').toBeTruthy();
  });

  it('Should download the zip successfully', async () => {
    // Start the download process
    const downloadPromise = Module.downloadCdn(testCdnUrl, mockOut);

    // Simulate the request lifecycle events
    mockRequest.triggerEvent('response', { statusCode: 200 });
    mockRequest.triggerEvent('data', Buffer.from('test file chunk'));
    mockRequest.triggerEvent('end');

    // Trigger the output stream's close event to resolve the Promise
    mockOut.triggerClose();

    // Wait for the promise to resolve and check results
    const [error, response] = await downloadPromise;

    expect(error).toBeNull();
    expect(response).toBe('Done');
    // Add extra robustness checks
    expect(request).toHaveBeenCalledWith({ method: 'GET', uri: testCdnUrl });
    expect(mockRequest.pipe).toHaveBeenCalledWith(mockOut);
  });
});

Key Improvements Explained

  • Mock Event Control: Both the request and mockOut objects now track event listeners and have helper methods to trigger events, letting us replicate the real download flow.
  • Test Isolation: The beforeEach hook resets mocks and creates fresh test objects for every test, preventing cross-test contamination.
  • Explicit Flow Simulation: We trigger the full request lifecycle (response, data, end) and then the output stream's close event—this is what finally resolves the Promise in downloadCdn, giving us the expected 'Done' response.
  • Robust Assertions: We added checks to ensure the request was called correctly and the pipe operation happened, making the test more thorough.

内容的提问来源于stack exchange,提问作者vadim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 08:38:56