使用webpack的worker-loader在Jest测试中解决导入问题

22

我正在编写 Jest 测试,并且在解析使用 webpack 的 worker-loader 的导入时遇到了问题。

import ImageInfoWorker from 'worker-loader?name=image_info!@WORKERS/image-info';

我有一些其他的别名在我的测试中正常解析,比如:

import Store from '@SUPPORT/store';
import * as api from '@SUPPORT/api';

以下是来自 package.json 的相关代码片段:

  "jest": {
    "moduleFileExtensions": ["js", "jsx"],
    "moduleNameMapper": {
      "^@CSS/(.*)$": "<rootDir>/css/$1",
      "^@COMPONENTS/(.*)$": "<rootDir>/js/components/$1",
      "^@MODELS/(.*)$": "<rootDir>/js/models/$1",
      "^@STORES/(.*)$": "<rootDir>/js/stores/$1",
      "^@SUPPORT/(.*)$": "<rootDir>/js/support/$1",
      "^(.*?)@WORKERS/(.*)$": "$1<rootDir>/js/workers/$2"
  }
}

这里是我webpack配置文件中的resolve部分:

        resolve: {
          extensions: ['.js', '.jsx'],
          modules: [process.env.NODE_PATH, 'node_modules'],
          alias: {
            '@CSS':        path.join(projectRoot, 'css'),
            '@COMPONENTS': path.join(projectRoot, 'js', 'components'),
            '@MODELS':     path.join(projectRoot, 'js', 'models'),
            '@STORES':     path.join(projectRoot, 'js', 'stores'),
            '@SUPPORT':    path.join(projectRoot, 'js', 'support'),
            '@WORKERS':    path.join(projectRoot, 'js', 'workers')
        }
      },

你找到解决问题的方法了吗? - JuHwon
@JuHwon 不,我们还没有。 - wuliwong
信息不足。您确定在测试中运行了webpack吗?问题在于worker-loader是webpack的内容,因此您确实需要在测试之前进行构建。另外,jest在node中运行,而node没有workers,因此您需要将worker设置为inline,在webpack中构建所有内容并运行测试。 - Eduard Jacko
你尝试过 https://github.com/developit/jsdom-worker 吗?这适用于普通的 Worker。我不确定使用 worker-loader 导入模块的代码是否可行。 - yiwen
我在一个React应用程序中遇到了同样的问题,在这里找到了解决方案。 - Jacob van Lingen
4个回答

6
如果您不需要解析路径,可以使用"moduleNameMapper"来忽略导入。
首先,创建一个包含export default''的空模块。
接下来,在您的package.json中添加以下内容:
"jest": {
    "moduleNameMapper": {
        "^worker-plugin/loader.+$": "<rootDir>/EmptyModule"
    }
}

4
这个解决方案对我很有用。
在一个名为__mocks__的文件夹中创建一个名为workerMock.js的文件,并编写所需的模拟实现(当然,你可以在任何地方创建它)。
module.exports = Object.create(null);

然后在你的 Jest 配置文件中添加以下配置:

"moduleNameMapper": {
  "\\.worker.js":"<rootDir>/__mocks__/workerMock.js"
}

2
根据 cpojer 的说法,Jest 不支持 Web Workers。你应该使用模拟,在这里 阅读更多信息。

1
为了被选为答案,你需要提供一个可行的解决方案。 - wuliwong

1
这种方法对于内联样式和配置样式的工作引入都适用。
Jest尚不支持Webpack打包的WebWorkers(至少我不知道),因此您需要模拟Worker。只需将工作人员的功能提取到外部文件中,并在工作人员文件中执行仅限于工作人员的任务即可。 @WORKERS/imageInfo.js - 您的工作人员的“核心”。
export default imageInfoFunction(data) {
    //...
}

@WORKERS/imageInfo.worker.js,这是一个工作线程:

import imageInfoFunction from "./imageInfo";

self.onmessage = async function (e) {
  self.postMessage(imageInfoFunction(e.data));
};

这样,您就可以在测试实际功能时模拟实现中的Worker部分:

mocks/imageInfo.worker.js

import imageInfoFunction from "@WORKERS/imageInfo";

export default class ImageInfoWorker {
  constructor() {
    // should be overwritten by the code using the worker
    this.onmessage = () => { };
  }

  // mock expects data: { } instead of e: { data: { } }
  postMessage(data) {
    // actual worker implementation wraps argument into { data: arg },
    // so the mock needs to fake it 
    this.onmessage({ data: imageInfoFunction (data) });
  }
}

现在在 jest.config.js 文件中:
module.exports = {
  moduleNameMapper: {
    "@WORKERS/(.*\\.worker\\.js)$": "<rootDir>/mocks/$1",
    "@WORKERS/(.*)$": "<rootDir>/js/workers/$1",
  },
};

注意,我没有包含内联的worker-loader配置,但我跳过了^(.*)。这是因为我们不再需要worker-loader,因为我们正在模拟worker。第一个路径是用于我们想要模拟的.worker.js文件,而另一个路径是用于我们想要测试的实际功能。以下内容也可以工作:
    "^(.*?)@WORKERS/(.*\\.worker\\.js)$": "<rootDir>/mocks/$2",
    "^(.*?)@WORKERS/(.*)$": "<rootDir>/js/workers/$2",

这个解决方案可能可以推广,使所有的工作人员都被模拟,欢迎提出建议。

我的@Workers/imageInfo.worker.js也有self.importScripts,但它没有被模拟,有什么办法可以解决吗?所以我的@Workers/imageInfo.worker.js看起来像这样从"./imageInfo"导入imageInfoFunction; self.importScripts("somescript");self.onmessage = async function (e) { self.postMessage(imageInfoFunction(e.data)); }; - Nikita Sharma

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接