如何使用 Jest 使用加密或 window.msCrypto 来测试函数

How to use Jest to test functions using crypto or window.msCrypto

当 运行 使用 Jest 进行单元测试时,window.crypto API 会导致问题。我还没有找到在不安装其他软件包的情况下将 crypto 合并到 Jest 中的方法,这是我做不到的。因此,在不使用另一个 npm 包的情况下,有没有一种方法可以测试使用的函数: crypto.getRandomValues() 在其中不会使 Jest 崩溃?感谢任何链接、建议或提示

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

const crypto = require('crypto');

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

为您的玩笑环境添加 crypto 全局,就像在浏览器中一样。 您的 jest.config.js 应如下所示:

const {defaults} = require('jest-config');

module.exports = {
  globals: {
    ...defaults.globals,
    crypto: require('crypto')
  }
};

参考:https://jestjs.io/docs/en/configuration#globals-object

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

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

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

import crypto from 'crypto'

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

我在 Angular 8 中遇到这个问题,使用 uuid 生成器对 lib 进行 Jest 测试。在开玩笑的测试设置中,我模拟了这个:

Object.defineProperty(global.self, 'crypto', {
  value: {
    getRandomValues: arr => arr
  },
});
const crypto = require('crypto');
global.crypto = crypto;

因为节点 15.x 你可以使用 crypto.webcrypto

例如

import crypto from "crypto";

Object.defineProperty(global.self, "crypto", {
  value: {
    subtle: crypto.webcrypto.subtle,
  },
});

源自 AIVeligs 答案:

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

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

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

module.exports = {
   ...
   setupFiles: [
      '<rootDir>/tests/settings/jest.crypto-setup.js',
   ],
};

并在 jest.crypto-setup.js 中:

global.crypto = { 
     getRandomValues: (arr) => require('crypto').randomBytes(arr.length) 
};

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

在使用 Jest 进行测试时,默认的 crypto 依赖项对我不起作用。

相反,我使用了 @peculiar/webcrypto 库:

yarn add -D @peculiar/webcrypto

然后在您的 Jest 安装文件中,只需添加:

import { Crypto } from "@peculiar/webcrypto";


window.crypto = new Crypto();

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

getRandomValues() 也不接受 Array 作为参数,它只接受整数 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 module (should be a matter of setting globalThis.crypto = require('crypto').webcrypto). If you're using an older version of Node (v14 or below) you might have better luck using crypto.randomFillSync() 解决了这个问题,它应该可以用作 getRandomValues() 的替代品,因为它修改了传递的缓冲区/TypedArray就地.

在你的 Jest 安装文件中(不能通过 globals configuration 设置,因为它只允许 JSON 兼容的值):

const { randomFillSync } = require('crypto')

Object.defineProperty(globalThis, 'crypto', {
  value: { getRandomValues: randomFillSync },
})

dspacejs 的 答案几乎对我有用,除了我遇到了与 Mozgor 相同的问题。我收到一条错误消息,指出 window.crypto 是只读的。您可以使用 Object.assign 而不是直接尝试覆盖它。

yarn add -D @peculiar/webcryptonpm i --save-dev @peculiar/webcrypto

安装@peculiar/webcrypto

然后将以下内容添加到您的 Jest 安装文件中:

import { Crypto } from "@peculiar/webcrypto";

Object.assign(window, {
  crypto: new Crypto(),
})

在默认配置中,Jest assumes you are testing a Node.js environment。但是,当您使用 window 对象的方法时遇到错误,您可能正在制作网络应用程序。

所以如果你正在制作一个网络应用程序,你应该使用“jsdom”作为你的“testEnvironment”。 为此,请将 "testEnvironment": "jsdom", 插入到您的 Jest 配置中。

如果您维护一个“jest.config.js”文件,则添加它:

module.exports = {
   ...
   "testEnvironment": "jsdom",
   ...
};

或者,如果您像我一样将 Jest 配置保存在“package.json”中:

{
    ...,
    "jest": {
        ...,
        "testEnvironment": "jsdom",
        ...
    },
    ...
}

根据其他人在这里提出的建议,我通过以下方式解决了 window.crypto.subtle.digest 的问题:

Object.defineProperty(global.self, "crypto", {
  value: {
    getRandomValues: (arr: any) => crypto.randomBytes(arr.length),
    subtle: {
      digest: (algorithm: string, data: Uint8Array) => {
        return new Promise((resolve, reject) =>
          resolve(
            createHash(algorithm.toLowerCase().replace("-", ""))
              .update(data)
              .digest()
          )
        );
      },
    },
  },
});

或者,如果不使用 Typescript:

Object.defineProperty(global.self, "crypto", {
  value: {
    getRandomValues: (arr) => crypto.randomBytes(arr.length),
    subtle: {
      digest: (algorithm, data) => {
        return new Promise((resolve, reject) =>
          resolve(
            createHash(algorithm.toLowerCase().replace("-", ""))
              .update(data)
              .digest()
          )
        );
      },
    },
  },
});

字符串的重新格式化是可选的。也可以对算法进行硬编码,例如通过声明 'sha256' 或 'sha512' 或类似内容。