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

如何使用Mocha模拟并测试FTP连接

如何用Mocha模拟并测试FTP连接逻辑?

你现在要测试这段FTP连接的业务代码,核心是不要依赖真实的FTP服务器,而是通过模拟FTP客户端的行为来验证逻辑是否符合预期。下面我给你一步步拆解具体的实现方式:

第一步:准备测试依赖

首先得装几个必要的测试工具,在项目里执行:

npm install mocha chai sinon proxyquire --save-dev
  • mocha是测试框架,负责跑测试用例
  • chai用来写断言,验证结果是否符合预期
  • sinon用来模拟类、方法和事件,替代真实的FTP客户端
  • proxyquire用来替换业务代码里的FTP Client依赖,让我们的模拟类生效

第二步:重构业务代码(可选但推荐)

原来的代码是直接执行的,不利于测试。建议把它抽成一个可调用的函数,方便在测试里调用:
比如新建ftp-service.js:

const Client = require('ftp'); // 假设你用的是ftp库

exports.connectAndList = () => {
  const client = new Client();
  const connectOpts = { host: 'host', port: 21, user: 'user', password: 'password' };
  
  client.on('ready', function() {
    client.list(function(err, list) {
      if (err) throw err;
      console.log(list);
      client.end();
    });
  });
  
  client.connect(connectOpts);
  return client;
};

第三步:编写Mocha测试用例

新建ftp-service.test.js,里面写测试逻辑:

const { expect } = require('chai');
const sinon = require('sinon');
const proxyquire = require('proxyquire');

describe('FTP连接与列表获取逻辑', () => {
  let mockClient;
  let ftpService;

  beforeEach(() => {
    // 1. 模拟FTP Client类
    mockClient = sinon.spy(function() {
      this.on = sinon.spy();
      this.connect = sinon.spy();
      this.list = sinon.spy();
      this.end = sinon.spy();
    });

    // 2. 用proxyquire替换真实的Client为我们的模拟类
    ftpService = proxyquire('./ftp-service', {
      'ftp': { Client: mockClient }
    });
  });

  it('应该使用正确的参数调用connect方法', () => {
    const expectedOpts = { host: 'host', port: 21, user: 'user', password: 'password' };
    ftpService.connectAndList();
    
    // 验证Client实例的connect方法被调用,且参数正确
    const clientInstance = mockClient.returnValues[0];
    expect(clientInstance.connect.calledWith(expectedOpts)).to.be.true;
  });

  it('ready事件触发后应该调用list方法', () => {
    ftpService.connectAndList();
    const clientInstance = mockClient.returnValues[0];
    
    // 获取ready事件的回调函数(on方法的第二个参数)
    const readyCallback = clientInstance.on.getCall(0).args[1];
    // 手动触发ready事件
    readyCallback();
    
    // 验证list方法被调用
    expect(clientInstance.list.calledOnce).to.be.true;
  });

  it('list成功时应该输出列表并调用end', () => {
    // 模拟console.log
    const consoleLogSpy = sinon.spy(console, 'log');
    ftpService.connectAndList();
    const clientInstance = mockClient.returnValues[0];
    
    // 触发ready事件
    const readyCallback = clientInstance.on.getCall(0).args[1];
    readyCallback();
    
    // 获取list方法的回调函数,手动传入成功结果
    const listCallback = clientInstance.list.getCall(0).args[0];
    const mockList = ['file1.txt', 'file2.jpg'];
    listCallback(null, mockList);
    
    // 验证console.log输出了列表,且end被调用
    expect(consoleLogSpy.calledWith(mockList)).to.be.true;
    expect(clientInstance.end.calledOnce).to.be.true;
    
    // 恢复console.log
    consoleLogSpy.restore();
  });

  it('list出错时应该抛出错误', () => {
    ftpService.connectAndList();
    const clientInstance = mockClient.returnValues[0];
    
    // 触发ready事件
    const readyCallback = clientInstance.on.getCall(0).args[1];
    readyCallback();
    
    // 获取list回调,传入错误
    const listCallback = clientInstance.list.getCall(0).args[0];
    const mockError = new Error('FTP list failed');
    
    // 验证抛出了错误
    expect(() => listCallback(mockError)).to.throw(mockError);
  });
});

第四步:运行测试

在package.json里加个测试脚本:

{
  "scripts": {
    "test": "mocha ftp-service.test.js"
  }
}

然后执行npm test就能看到测试结果了。

关键思路说明:

  • 我们完全模拟了FTP Client的所有方法和事件,不需要连接真实服务器,测试速度快且稳定
  • 用sinon.spy来跟踪方法的调用情况,验证参数、调用次数是否符合预期
  • 用proxyquire替换业务代码里的真实依赖,让模拟类生效
  • 通过手动触发事件和回调,控制测试流程的各个环节

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.28 10:19:06