如何使用Jest使用crypto或window.msCrypto测试功能

Cod*_*Lee 1 javascript reactjs jestjs window.crypto

当使用Jest进行运行单元测试时,window.crypto API会引起问题。我还没有找到一种在不安装其他软件包的情况下将加密合并到Jest中的方法,这是我做不到的。因此,无需使用其他npm软件包,就可以测试使用的功能:crypto.getRandomValues()在其中不会崩溃的Jest?任何链接,建议或技巧,不胜感激

Mic*_*ael 22

对于nodeJS + typescript,只需使用global而不是global.self

import crypto from 'crypto'

Object.defineProperty(global, 'crypto', {
  value: {
    getRandomValues: (arr:any) => crypto.randomBytes(arr.length)
  }
});
Run Code Online (Sandbox Code Playgroud)


小智 9

像@RwwL 一样,接受的答案对我不起作用。我发现这个库中使用的 polyfill 确实有效:commit with polyfill

//setupTests.tsx
const nodeCrypto = require('crypto');
window.crypto = {
  getRandomValues: function (buffer) {
    return nodeCrypto.randomFillSync(buffer);
  }
};
Run Code Online (Sandbox Code Playgroud)
//jest.config.js
module.exports = {
 //...
  setupFilesAfterEnv: ["<rootDir>/src/setupTests.tsx"],
};
Run Code Online (Sandbox Code Playgroud)


Vic*_*len 9

当前答案中的填充是不完整的,因为Crypto.getRandomValues()就地修改了它的参数并返回它。const foo = new Int8Array(8); console.log(foo === crypto.getRandomValues(foo))您可以通过在浏览器控制台中运行类似的命令来验证这一点,该控制台将打印true.

getRandomValues()也不接受 anArray作为其参数,它只接受整数TypedArrays。Node.js 的crypto.randomBytes()函数不适合这个 polyfill,因为它输出原始字节,而getRandomValues()可以接受元素最多为 32 位的有符号整数数组。如果您crypto.getRandomValues(new Int32Array(8))在浏览器中尝试,您可能会看到类似的内容[ 304988465, -2059294531, 229644318, 2114525000, -1735257198, -1757724709, -52939542, 486981698 ]。但如果您尝试node -e 'console.log([...require("crypto").randomBytes(8)])'使用命令行,您可能会看到[ 155, 124, 189, 86, 25, 44, 167, 159 ]. 显然,这些并不等效,并且如果使用后者进行测试,您的被测组件可能不会按预期运行。

最新版本的 Node.js 通过webcrypto模块解决了这个问题(应该是设置问题globalThis.crypto = require('crypto').webcrypto)。如果您使用旧版本的 Node(v14 或更低版本),您可能会更好地使用crypto.randomFillSync(),它应该可以用作 的直接替代品,因为它会就地getRandomValues()修改传递的缓冲区/ 。TypedArray

在 Jest 设置文件中(无法通过globals配置进行设置,因为它只允许 JSON 兼容的值):

const { randomFillSync } = require('crypto')

Object.defineProperty(globalThis, 'crypto', {
  value: { getRandomValues: randomFillSync },
})
Run Code Online (Sandbox Code Playgroud)


ayZ*_*gen 9

jsdom对于使用( jest-environment-jsdom) 环境的用户,Jest >=28您应该将替换模块定义为getter

//jest.config.js
module.exports = {
  testEnvironment: "jsdom",
  rootDir: './',
  moduleFileExtensions: ['ts', 'js'],
  setupFilesAfterEnv: ["<rootDir>/test/setup-env.tsx"],
  preset: 'ts-jest',
};
Run Code Online (Sandbox Code Playgroud)
// setup-env.tsx
const { Crypto } = require("@peculiar/webcrypto");
const cryptoModule = new Crypto();

Object.defineProperty(window, 'crypto', {
  get(){
    return cryptoModule
  }
})
Run Code Online (Sandbox Code Playgroud)

我正在使用@peculiar/webcrypto,但其他实现也应该可以工作。


mar*_*a21 7

我正在使用 vue-jest,对我有用的是文件中的以下配置jest.config.js

module.exports = {
   ...
   setupFiles: [
      '<rootDir>/tests/settings/jest.crypto-setup.js',
   ],
};
Run Code Online (Sandbox Code Playgroud)

并在jest.crypto-setup.js

global.crypto = { 
     getRandomValues: (arr) => require('crypto').randomBytes(arr.length) 
};
Run Code Online (Sandbox Code Playgroud)

getRandomValues直接添加函数定义module.exports不起作用,因为globals对象必须是 json 可序列化的(如此处指定的: https: //jestjs.io/docs/configuration#globals-object)。


obi*_*ahn 6

来自 AIVeligs 的答案:

由于我在 Jest 中使用“节点”环境,所以我必须使用

module.exports = {
  preset: "ts-jest",
  testEnvironment: "node",
  globals: {
    crypto: {
      getRandomValues: (arr) => require("crypto").randomBytes(arr.length),
    },
  },
};
Run Code Online (Sandbox Code Playgroud)


Har*_*dha 5

这应该做。使用以下代码crypto全局设置属性。这将允许Jest访问,window.crypto并且不会引起任何问题。

const crypto = require('crypto');

Object.defineProperty(global.self, 'crypto', {
  value: {
    getRandomValues: arr => crypto.randomBytes(arr.length),
  },
});
Run Code Online (Sandbox Code Playgroud)

  • 这看起来很简单,但对我来说不起作用;我的测试继续失败,并出现相同的 crypto.getRandomValues()-not-supported 消息。这有什么常见的问题吗? (3认同)
  • 如果您将 jest 更新到 v29 (https://jestjs.io/docs/upgrading-to-jest29#jsdom-upgrade),该问题即可解决。我们不再需要嘲笑加密货币 (3认同)
  • 加密货币的微妙之处怎么样?您可以举一个扩展您答案的示例,例如当您需要生成加密密钥对等时 (2认同)
  • 最好在 setupTests.js 中执行此操作,而不是在测试文件中执行此操作。 (2认同)

pom*_*pom 5

从节点 15.x 开始,您可以使用 crypto.webcrypto

例如。

import crypto from "crypto";

Object.defineProperty(global.self, "crypto", {
  value: {
    subtle: crypto.webcrypto.subtle,
  },
});
Run Code Online (Sandbox Code Playgroud)