使用 sinon、mocha 和 swc 对导出函数进行存根失败

问题描述 投票:0回答:2

我正在尝试存根从我的 ES 模块导出的函数。

我使用通配符导入(

import * as
)来存根它,并且当使用 ts-node 进行转译时它可以工作(
mocha --require ts-node/register */**/*.spec.ts
)。

但是当使用swc时,它会失败并显示以下消息(

mocha --require @swc/register */**/*.spec.ts
)。

TypeError: Descriptor for property validate is non-configurable and non-writable
/* hash.ts */
import * as argon2 from 'argon2'

export async function encrypt(plain) {
  return await argon2.hash(plain)
}

export async function validate(hash, plain) {
  return await argon2.verify(hash, plain)
}
/* service.ts */
import { validate } from './hash'

export async function isValidUser(user: User, password: string) {
  if (!user || !(await validate(user.password, password))) {
    return false
  }
  return true
}
/* service.spec.ts */
import * as hash from './hash'
import { isValidUser } from './service'
import { stub } from 'sinon'

describe('isValidUser', () => {
  stub(hash, 'validate').callsFake(
    async (passwordFormDB, passwordFromUserInput) =>
      passwordFromDB === passwordFromUserInput
  )

  it('...', async () => {
    /* test `isValidUser` function */
  })
})
// .swcrc
{
  "test": ".*.ts$",
  "jsc": {
    "parser": {
      "syntax": "typescript",
      "decorators": true,
      "importMeta": true
    },
    "transform": {
      "legacyDecorator": true,
      "decoratorMetadata": true
    },
    "paths": {
      "src/*": ["./src/*"]
    }
  },
  "module": {
    "type": "commonjs",
    "noInterop": true
  }
}
javascript typescript mocha.js sinon swc-compiler
2个回答
1
投票

由于 Sinon bug tracker 上刚刚出现了类似的问题,我决定深入研究一下是否可以解决它。事实证明这并不难,但如果您想了解背景的一些详细信息,您应该查看问题。我只是将相关部分复制粘贴到此处。

诗乃会嘲笑、存根和间谍。这些都是普通的、普通的函数,我们不会对运行时做任何魔法,所以无论要做什么都需要在给定的运行时使用普通的 Javascript 来完成。诗农清楚地说明了问题所在:

属性验证的描述符不可配置且不可写

如果转译的代码限制每个人修改这些导出,Sinon 自己就无法做任何事情。这个问题不是Sinon的问题,而是你的转译器工具的问题。

虽然

ts-node
明确导出可写对象描述符,但 SWC 却没有。这与 ES 模块的工作方式一致,因此 SWC 似乎在这里显示了正确的行为

解决方案

由于运行时不使用 ESM,而是使用 SWC 转译后的 CommonJS,因此您可以对 CommonJS 使用任何正常的 link seam 方法,基本上可以拦截模块加载。 Sinon 主页列出了一种这样的方法,即使用 Proxiquire。您还可以使用 Rewire、Quibble(TestDouble 的)或其他基本上具有相同功能的工具。

describe('isValidUser', () => {
    const mySpy = sinon.spy(async (passwordFormDB, passwordFromUserInput) =>
        passwordFromDB === passwordFromUserInput)
    quibble('./hash', { validate: mySpy });

    it('...', async () => {
        /* test `isValidUser` function */
    })
})

链接的问题还展示了如果您不使用 TypeScript,而是运行未转译的 ESM,则如何通过使用 Quibble 作为模块加载器来替换模块。它还解释了为什么当前使用 TypeScript 定位 ES 模块 (

"moduleResolution": "Node16"
) 时很难(并非不可能)模拟模块,因为您需要编写一个委托加载器来处理文件名解析,然后再移交给 Quibble。


-1
投票

ts-node 在 typescript 3.8 之后也不起作用

© www.soinside.com 2019 - 2024. All rights reserved.