使用 Jest 进行测试时如何使用 `import.meta`

Posted

技术标签:

【中文标题】使用 Jest 进行测试时如何使用 `import.meta`【英文标题】:How to Use `import.meta` When Testing With Jest 【发布时间】:2021-03-05 18:04:42 【问题描述】:

我正在使用 ESModules 编写 Node.js 代码(在 TypeScript 中),我需要访问 __dirname。为了在 CommonJS 中访问等效于 __dirname 的 ESM,我调用了 dirname(fileURLToPath(import.meta.url))。我还在 Jest 中使用 TypeScript 编写测试。使用this 指南,我设置了 Babel。当我运行jest 命令时,我得到了

const DIRNAME = (0, _path.dirname)((0, _url.fileURLToPath)(import.meta.url));
                                                                      ^^^^
SyntaxError: Cannot use 'import.meta' outside a module

这是我写的文件

someCode.ts:

import  dirname  from "path";
import  fileURLToPath  from "url";

const DIRNAME = dirname(fileURLToPath(import.meta.url));

export const x = 5;

someCode.test.ts:

import  x  from "./someCode";

it("Returns 5 as the result", () => 
    expect(x).toEqual(5);
);

.babelrc.json:


    "presets": [
        ["@babel/preset-env",  "targets":  "node": "current"  ],
        "@babel/preset-typescript"
    ]

tsconfig.json:


    "compilerOptions": 
        "target": "ES2020",
        "module": "ESNext",
        "moduleResolution": "node"
    ,
    "include": ["./src/**/*.ts", "./src/**/*.js"]

package.json:


    "name": "test",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "type": "module",
    "scripts": 
        "test": "jest"
    ,
    "keywords": [],
    "author": "",
    "license": "ISC",
    "devDependencies": 
        "@babel/core": "^7.12.7",
        "@babel/preset-env": "^7.12.7",
        "@babel/preset-typescript": "^7.12.7",
        "jest": "^26.6.3",
        "typescript": "^4.1.2"
    

环境:

节点:14.1.0 查看package.json 了解模块版本

【问题讨论】:

您正在遵循使用 CommonJS 模块的通用指南。对于原生 ESM,请参阅 jestjs.io/docs/en/ecmascript-modules 我正在寻找相同问题的答案。将 Jest 移至 ESM 模块似乎太过分了。 【参考方案1】:

我们使用import.meta.url 来使用网络工作者,例如使用native worker support in Webpack 5。这很好用,但在运行 Jest 测试时会失败。

据我所知,

babel-vite-presetnot 会处理这个问题。关于提取和模拟使用import.meta 的代码的最佳答案确实有效,但很笨拙。

我发现目前最好的解决方案是使用babel-plugin-transform-import-meta。这个简单的插件用 Node.js 代码替换了 import.meta.url 语法,以检索当前文件的 URL,因此它在测试中运行良好。请注意,您只希望在运行测试时激活此插件。

【讨论】:

谢谢,正是我的需要!【参考方案2】:

解决方案:模块化 ESM 代码并模拟它。

我最近也遇到了这个问题,我最终不得不:

    导出 ESM 特定问题,例如import.meta 功能,进入它自己的“utils”文件/函数。 然后在 mocks 目录中创建一个不需要 ESM 特定功能的模拟函数。 在使用该功能的文件中,声明jest.mock("/path/to/that/file.ts")

该过程将如下所示:

原始文件结构

src/
--/someCode.ts
--/__tests__/
/--/--/someCode.test.ts
// someCode.ts
...
const getApiUrl = () =>  
  ...
  const url = import.meta.env.SOME_ENV_VAR_HERE;
  ...
  return url;
;
...

新的文件结构

标题

src/
--/someCode.ts
--/utils.js
--/__mocks__/
--/--/utils.ts
--/__tests__/
--/--/someCome.test.ts

然后在文件本身中:

// someCode.ts
...
import  getApiUrl  from "./utils.ts";
...

// utils.js
export const getApiUrl = () => ...;

对于测试:

// __mocks__/utils.ts
export const getpiUrl = jest.fn(() => 'some.url');

// __tests__/utils.test.ts
...
jest.mock("src/util.ts");
...
describe(...);

【讨论】:

这就是我的解决方案。唯一的区别是做这样的事情:``` jest.mock('$lib/variables', () => ( __esModule: true, variables: apiUrl: 'localhost:2222' )); ``` @TimoHermans 您能否在回答中发布适合您的解决方案【参考方案3】:

在您的tsconfig.json 内更改为

    
    "compilerOptions": 
        "target": "ES2020",
        "module": "ES2020",
        "moduleResolution": "node"
    ,
    "include": ["./src/**/*.ts", "./src/**/*.js"]

【讨论】:

【参考方案4】:

babel-vite-preset

我觉得这个不错。

非官方的。

您可以选择仅使用它babel-plugin-transform-vite-meta-env 插件或使用整个预设并设置如下配置


  "presets": [
    [
      "babel-preset-vite",
      
        "env": true, // defaults to true
        "glob": false // defaults to true
      
    ]
  ]

【讨论】:

这对我有用。唯一的问题是,当访问 envglob 以外的其他属性时,例如 import.meta.hot,它将无法正常工作【参考方案5】:

我也遇到了这个问题。我能够通过在 babel 处理期间动态插入正确的值来解决这个问题。我没有使用建议的search-and-replace babel 插件,而是使用了codegen macro for babel。

// someCode.ts
import  dirname  from "path";
import  fileURLToPath  from "url";
import codegen from 'codegen.macro';

const DIRNAME = dirname(fileURLToPath(codegen`module.exports = process.env.NODE_ENV === "test" ? "ADD VALID VALUE FOR TESTS" : "import.meta.url"`));

export const x = 5;

【讨论】:

【参考方案6】:

我遇到了同样的问题,我用 babel 插件解决了它:search-and-replace

在我的代码中,我将所有 import.meta.url 更改为 import_meta_url

我将插件添加到 babel config 以在开发和生产中通过 import.meta.urlresolve(__dirname, 'workers') 更改它

我能够这样做是因为它符合我所有的情况,如果您的 import_meta_url 需要在不同情况下用不同的刺替换,您将需要使用 import_meta_url_1 import_meta_url_2 等例如

终于是我的babel.config.js

const  resolve  = require('path');

module.exports = (api) => 
  api.cache.using(() => process.env.NODE_ENV);

  if (process.env.NODE_ENV === 'test') 
    return 
      presets: ['next/babel'],
      plugins: [
        [
          'search-and-replace',
          
            rules: [
              
                searchTemplateStrings: true,
                search: 'import_meta_url',
                replace: resolve(__dirname, 'workers'),
              ,
            ],
          ,
        ],
      ],
    ;
  

  return 
    presets: ['next/babel'],
    plugins: [
      [
        'search-and-replace',
        
          rules: [
            
              searchTemplateStrings: true,
              search: 'import_meta_url',
              replace: 'import.meta.url',
            ,
          ],
        ,
      ],
    ],
  ;
;

GL

【讨论】:

我在我的 babel.config.js 中使用了相同的配置,但它不起作用。我收到以下错误TypeError: Failed to construct 'URL': Invalid base URL

以上是关于使用 Jest 进行测试时如何使用 `import.meta`的主要内容,如果未能解决你的问题,请参考以下文章

使用 Jest 进行 React js 测试失败

如何在 Jest 测试中使用我的 webpack 的 html-loader 导入?

笑话:不能在模块外使用 import 语句

如何使用 Jest 测试样式

使用 Jest、AudioContext 进行测试和模拟

如何模拟在使用 Jest 测试的 React 组件中进行的 API 调用