你写测试吗? jest.spyOn 做完的时候不就是这样写的吗?

import * as f from './functions'

jest.spyOn(f, "x").mockImplementation(() => 'x')
test('should be x', () => expect(f.x()).toBe('x'))

写js的时候,经常会创建一个函数的模块,而不是类基,写mock()比较麻烦,所以偷工减料,这样写,但是对于esm→cjs文件的玩笑,依赖于测试和组合转译器,就会出现标题错误。

TL;博士

  • 如果你使用 babel-jest、tsc-jest,你应该没问题。如果 babel/tsc 发出符合 esm 的 cjs,应该会发生错误。
  • 使用 swc-jest、esbuild-jest 时出错。
  • 对于 swc-jest,使用 jest_workaround 插件(使导出的内容可配置的插件)。
  • 使用另一个测试框架(vitest 很好)。
  • 乖乖使用mock()。

背景

当nextjs产品从babel中移除迁移到swc,jest测试也改为swc-jest

TypeError:无法重新定义属性

我遇到了一个错误并尝试了各种方法。

是什么原因

只看 jest 的实现和每个转译器的结果。

你在 esm 中写的

export const a = () => {}

当转换为 cjs

babel/tsc(大致相同)

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.a = void 0;
const a = () => { };
exports.a = a;

swc/esbuild(大致相同)

"use strict";
Object.defineProperty(exports, "__esModule", {
    value: true
});
Object.defineProperty(exports, "a", {
    enumerable: true,
    get: function() {
        return a;
    }
});
var a = function() {};

笑话实现

let descriptor = Object.getOwnPropertyDescriptor(object, methodKey);
...
if (descriptor && descriptor.get) {
  const originalGet = descriptor.get;
  mock = this._makeComponent({type: 'function'}, () => {
      descriptor!.get = originalGet;
      Object.defineProperty(object, methodKey, descriptor!);
      });
  descriptor.get = () => mock;
  Object.defineProperty(object, methodKey, descriptor);
} else {
  mock = this._makeComponent({type: 'function'}, () => {
      if (isMethodOwner) {
      object[methodKey] = original;
      } else {
      delete object[methodKey];
      }
      });
  // @ts-expect-error overriding original method with a Mock
  object[methodKey] = mock;
}

就是带有descriptor.get的那个,也就是swc/esbuild做成cjs的那个。转译器的结果是符合 esm 的,并且可配置为默认值 false,因此在第 7 行会出现无法重新定义属性错误。

另一方面,babel/tsc 没有定义 Object.defineProperty 并按原样导出,所以没有错误,因为 jest 实现的 else 端没有 Object.defineProperty。

然后怎么办

在 swc/esbuild 中有很多这样的问题,因为更改编译器会导致错误。不过双方的说法是,esm本来就是不可变的,所以babel/tsc就奇怪了,他们也不知道。在下面的评论中,一个 swc 提交者创建了一个使 esm 可变的插件,但它已被弃用。

乖乖地用mock来写,好像也不错。或者另外一个测试框架,部分团队使用vitest,转译器是esbuild,但是这似乎不会导致这个错误,因为spyOn实现不同。

甚至 vitest 的 spyOn 实现也与 jest 不同,所以有一种现象是它在 jest 中有效,而在 vitest 中无效,但我将把这个留到另一篇文章中。


原创声明:本文系作者授权爱码网发表,未经许可,不得转载;

原文地址:https://www.likecs.com/show-308628299.html

相关文章: