[英]How to use Jest to test functions using crypto or window.msCrypto
當使用 Jest 運行單元測試時, window.crypto
API 會引起問題。 我還沒有找到一種在 Jest 中合並crypto
而不安裝其他軟件包的方法,這是我做不到的。 因此,在不使用另一個 npm package 的情況下,有沒有一種方法可以測試使用的函數: crypto.getRandomValues()
在其中不會使 Jest 崩潰? 感謝任何鏈接、建議或提示
這應該做。 使用以下代碼全局設置crypto
屬性。 這將允許 Jest 訪問window.crypto
並且不會導致任何問題。
const crypto = require('crypto');
Object.defineProperty(global.self, 'crypto', {
value: {
getRandomValues: arr => crypto.randomBytes(arr.length)
}
});
像@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"],
};
從節點 15.x 開始,您可以使用crypto.webcrypto
例如。
import crypto from "crypto";
Object.defineProperty(global.self, "crypto", {
value: {
subtle: crypto.webcrypto.subtle,
},
});
對於那些使用Jest >=28
的jsdom
( jest-environment-jsdom
) 環境的人,你應該將替換模塊定義為getter 。
//jest.config.js
module.exports = {
testEnvironment: "jsdom",
rootDir: './',
moduleFileExtensions: ['ts', 'js'],
setupFilesAfterEnv: ["<rootDir>/test/setup-env.tsx"],
preset: 'ts-jest',
};
// setup-env.tsx
const { Crypto } = require("@peculiar/webcrypto");
const cryptoModule = new Crypto();
Object.defineProperty(window, 'crypto', {
get(){
return cryptoModule
}
})
我正在使用@peculiar/webcrypto
但其他實現也應該有效。
為您的 jest 環境添加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
源自 AIVeligs 答案:
由於我在 Jest 中使用了“節點”環境,因此我不得不使用
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
globals: {
crypto: {
getRandomValues: (arr) => require("crypto").randomBytes(arr.length),
},
},
};
我正在使用 vue-jest,對我jest.config.js
是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-serializable(如此處指定: https : module.exports
)。
當前答案中的 polyfill 不完整,因為Crypto.getRandomValues()
就地修改了它的參數並返回它。 您可以通過運行類似const foo = new Int8Array(8); console.log(foo === crypto.getRandomValues(foo))
來驗證這一點。 const foo = new Int8Array(8); console.log(foo === crypto.getRandomValues(foo))
在您的瀏覽器控制台中,它將打印true
。
getRandomValues()
也不接受Array
作為其參數,它只接受整數TypedArray
s 。 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 },
})
對於 nodeJS + 打字稿,只需使用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;
在使用 Jest 進行測試期間,默認的crypto
依賴項對我不起作用。
相反,我使用了@peculiar/webcrypto
庫:
yarn add -D @peculiar/webcrypto
然后在您的 Jest 設置文件中,添加以下內容:
import { Crypto } from "@peculiar/webcrypto";
window.crypto = new Crypto();
基於其他人在這里提出的建議,我通過以下方式解決了 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”等。
dspacejs 的答案幾乎對我有用,除了我和Mozgor有同樣的問題。 我收到一條錯誤消息,說 window.crypto 是只讀的。 您可以使用 Object.assign 而不是直接嘗試覆蓋它。
使用yarn add -D @peculiar/webcrypto
或npm i --save-dev @peculiar/webcrypto
然后將以下內容添加到您的 Jest 設置文件中:
import { Crypto } from "@peculiar/webcrypto";
Object.assign(window, {
crypto: new Crypto(),
})
在默認配置中, Jest 假設您正在測試 Node.js 環境。 但是當您使用window
對象的方法遇到錯誤時,您可能正在制作一個 Web 應用程序。
所以如果你正在制作一個網絡應用程序,你應該使用“jsdom”作為你的“testEnvironment”。 為此,請將"testEnvironment": "jsdom",
插入到您的 Jest 配置中。
如果您維護一個“jest.config.js”文件,則將其添加如下:
module.exports = {
...
"testEnvironment": "jsdom",
...
};
或者,如果像我一樣,您將 Jest 配置保存在“package.json”中:
{
...,
"jest": {
...,
"testEnvironment": "jsdom",
...
},
...
}
依賴注入是解決這個問題的一種方法。
Node.js 提供標准 Web Crypto API 的實現。使用 require('node:crypto').webcrypto 訪問此模塊。
因此,您將密碼 object 傳遞給依賴它的代碼。
請注意我們在調用方法 utils.aesGcmEncrypt 時如何“注入”正確的密碼utils.aesGcmEncrypt
test("Encrypt and decrypt text using password", async () => {
const password = "Elvis is alive";
const secret =
"surprise action festival assume omit copper title fit tower will chalk bird";
const crypto = require("crypto").webcrypto;
const encrypted = await utils.aesGcmEncrypt(crypto, secret, password);
const decrypted = await utils.aesGcmDecrypt(crypto, encrypted, password);
expect(decrypted).toBe(secret);
});
晚會遲到了,但我通常會這樣做:
// module imports here
// important: the following mock should be placed in the global scope
jest.mock('crypto', function () {
return {
randomBytes: jest
.fn()
.mockImplementation(
() =>
'bla bla bla'
),
}
});
describe('My API endpoint', () => {
it('should work', async () => {
const spy = jest.spyOn(DB.prototype, 'method_name_here');
// prepare app and call endpoint here
expect(spy).toBeCalledWith({ password: 'bla bla bla' });
});
});
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.