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

Electron环境Jest测试报错:unzipSync及fs.rm不是函数

Electron Jest测试中fflate与fs-extra函数失效问题

在Electron环境中用Jest执行单元测试时遇到两个异常:

  • 调用fflate的unzipSync时抛出错误:TypeError: (0 , _fflate.unzipSync) is not a function,该函数在生产环境运行正常,仅测试环境失效。
  • 使用fs-extra的remove方法时触发fs.rm is not a function错误,但readFile等其他方法可正常使用。所有功能在实际应用中均正常,仅Jest测试失败,询问是否需要调整测试配置。

业务代码

import {
  copy,
  emptyDir,
  outputFile,
  pathExists,
  readFile,
  remove,
} from 'fs-extra';

import { IReadProfileParams } from './types';
import { join } from 'path';
import { unzipSync } from 'fflate';

async function readProfile(params: IReadProfileParams) {
  try {
    const { profilePath, tmpPath, resPath, tempResPath } = params;
    const profileData = await readFile(profilePath);
    const unzipped = unzipSync(profileData);

    // 复制资源文件
    await emptyDir(tmpPath);
    const promises = [];
    for (const key of Object.keys(unzipped)) {
      if (unzipped[key].length) {
        const filePath = join(tmpPath, key);
        promises.push(outputFile(filePath, unzipped[key]));
      }
    }
    await Promise.all(promises);
    // 迁移资源目录
    await copy(resPath, tempResPath, { overwrite: true });
    await remove(resPath);
   } catch (e) {
    throw new Error(e);
   }
  }

jest.config.js配置

module.exports = {
  collectCoverageFrom: ['<rootDir>/src/renderer/src/utils/**/index.ts'],
  moduleNameMapper: {
    '@/(.*)': '<rootDir>/src/renderer/src/$1',
    'src/renderer/src/constants/path.ts': '<rootDir>/mocks/func.js',
  },
  runner: 'jest-electron/runner',
  testEnvironment: 'jest-electron/environment',
  testMatch: ['<rootDir>/src/renderer/**/*.test.ts'],
  setupFiles: ['./test/setup.js'],
};

setup.js配置

import * as fflate from 'fflate';
const fkill = require('fkill');
const { app } = require('./electron/app');

exports.mochaGlobalSetup = async () => {
  await app.start();
  app.browserWindow.focus();
  app.browserWindow.setAlwaysOnTop(true);
};

exports.mochaGlobalTeardown = async () => {
  if (app && app.isRunning()) {
    await app.stop();
  }
  await fkill(":8238", {
    force:true
  });
};

触发fs.rm错误的简化测试代码

import {
  copy,
  emptyDir,
  outputFile,
  pathExists,
  readFile,
  remove,
} from 'fs-extra';

import { IReadProfileParams } from './types';
import { join } from 'path';
import { unzipSync } from 'fflate';

async function readProfile(params: IReadProfileParams) {
  try {
    const { profilePath, tmpPath, resPath, tempResPath } = params;
    const profileData = await readFile(profilePath);
    await remove(resPath);
  } catch (e) {
    throw new Error(e);
  }
}

解决方案

针对fflate的unzipSync失效问题

  1. 强制使用CommonJS版本:fflate默认提供ESM格式,Jest处理时可能存在兼容问题。在jest.config.js的moduleNameMapper中添加映射:
moduleNameMapper: {
  // 保留原有配置
  'fflate': '<rootDir>/node_modules/fflate/cjs/index.js'
}
  1. 调整transform规则:如果使用babel-jest,在jest.config.js中添加配置排除fflate的转译:
transformIgnorePatterns: ['/node_modules/(?!fflate)/']

针对fs-extra remove方法触发fs.rm错误问题

  1. 升级jest-electron:fs.rm是Node.js 14.14+新增API,旧版本jest-electron的Node环境版本较低,升级至最新版可解决该问题。
  2. 手动Polyfill fs.rm:在setup.js中添加兼容实现:
const fs = require('fs');
if (!fs.rm) {
  fs.rm = function(path, opts, callback) {
    if (typeof opts === 'function') {
      callback = opts;
      opts = {};
    }
    // 模拟rm逻辑,根据需求选择unlink或rmdir
    fs.unlink(path, callback);
  };
}
  1. Mock remove方法:在测试文件中直接mockfs-extra的remove方法,避免调用底层API:
jest.mock('fs-extra', () => ({
  ...jest.requireActual('fs-extra'),
  remove: jest.fn().mockResolvedValue(undefined)
}));

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 13:30:00