When running unit tests with Jest in react the window.crypto
API is causing problems. I haven't found a way to incorporate crypto
in Jest without installing other packages which is something I can't do. So without using another npm package is there a way to test functions that use: crypto.getRandomValues()
in them that doesn't crash Jest? Any links, advice, or tips are appreciated
-
If you update jest to v29 (https://jestjs.io/docs/upgrading-to-jest29#jsdom-upgrade) that issue is resolved. We don't need to mock crypto anymore – Nacho Zullo Jun 15 '23 at 12:40
18 Answers
Use the following code to set up the crypto
property globally. It will allow Jest to access
window.crypto
in the browser environmentglobal.crypto
in non-browsers environments. (Node/Typescript scripts).
It uses the globalThis
which is now available on most of the latest browsers as well as Node.js 12+
const crypto = require('crypto');
Object.defineProperty(globalThis, 'crypto', {
value: {
getRandomValues: arr => crypto.randomBytes(arr.length)
}
});

- 12,098
- 3
- 36
- 40
-
7This seems so straightfoward but isn't working for me; my test continues to fail with the same crypto.getRandomValues()-not-supported message. Any common gotchas for this? – RwwL Apr 26 '20 at 23:43
-
-
2How about subtle of Crypto? You could give an example that expands on your answer like when you need to generate a crypto key pair or so – Paul Meyer Mar 15 '21 at 15:50
-
1Hi, I am also stuck in the same issue with crypto.subtle.digest, can someone please help? – Hari Om Aug 19 '21 at 07:59
-
`getRandomValues` doesn't return the values, it fills a typed array with values. Use mitchelc's answer. – benbotto Oct 12 '21 at 22:19
-
This has recently begun failing for me with the error "Value of "this" must be of type Crypto" – TemporaryFix Dec 24 '22 at 18:35
-
If you update jest to v29 (https://jestjs.io/docs/upgrading-to-jest29#jsdom-upgrade) that issue is resolved. We don't need to mock crypto anymore – Nacho Zullo Jun 15 '23 at 12:41
-
-
Like @RwwL, the accepted answer did not work for me. I found that the polyfill used in this library did work: 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"],
};

- 381
- 3
- 4
-
1
-
@mitchelc I got this error after configuring this " TypeError: crypto.getRandomValues is not a function". – Yatin Mistry Aug 17 '21 at 12:06
-
I used a similar approach in a test setup function: `if (!("crypto" in globalThis)) globalThis.crypto = require("crypto");` – Joe Maffei Sep 30 '22 at 17:20
Since node 15.x you can use crypto.webcrypto
eg.
import crypto from "crypto";
Object.defineProperty(global.self, "crypto", {
value: {
subtle: crypto.webcrypto.subtle,
},
});

- 423
- 5
- 10
For nodeJS + typescript, just use global
instead of global.self
import crypto from 'crypto'
Object.defineProperty(global, 'crypto', {
value: {
getRandomValues: (arr:any) => crypto.randomBytes(arr.length)
}
});

- 792
- 1
- 7
- 14
-
1After updating to Create React App v5.0.0, `global.self` stopped working for me (it did before). Removing `.self` fixed it. – Victor Feb 11 '22 at 18:49
-
1The other answers did not work for me. This answer works with nodeJs 18, create-react-app (react-scripts 5.0.1) with typescript (4.9.5). I used it for randomUII() instead of getRandomValues() by replacing getRandomValues with `randomUUID: () => crypto.randomUUID()`. Just add this answer to setupTests.ts – Welcor Feb 25 '23 at 20:23
The polyfills in the current answers are incomplete, since Crypto.getRandomValues()
modifies its argument in-place as well as returning it. You can verify this by running something like const foo = new Int8Array(8); console.log(foo === crypto.getRandomValues(foo))
in your browser console, which will print true
.
getRandomValues()
also does not accept an Array
as its argument, it only accepts integer TypedArray
s. Node.js' crypto.randomBytes()
function is not appropriate for this polyfill, as it outputs raw bytes, whereas getRandomValues()
can accept signed integer arrays with elements up to 32 bits. If you try crypto.getRandomValues(new Int32Array(8))
in your browser, you might see something like [ 304988465, -2059294531, 229644318, 2114525000, -1735257198, -1757724709, -52939542, 486981698 ]
. But if you try node -e 'console.log([...require("crypto").randomBytes(8)])'
on the command line, you might see [ 155, 124, 189, 86, 25, 44, 167, 159 ]
. Clearly these are not equivalent, and your component under test might not behave as expected if tested with the latter.
The latest versions of Node.js solve this problem with the 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()
, which should be useable as a drop-in replacement for getRandomValues()
as it modifies a passed buffer/TypedArray
in-place.
In your Jest setup file (can't be set via the globals
configuration as it only allows JSON-compatible values):
const { randomFillSync } = require('crypto')
Object.defineProperty(globalThis, 'crypto', {
value: { getRandomValues: randomFillSync },
})

- 121
- 1
- 2
Deriving from AIVeligs answer:
Since I use "node" environment in Jest I had to use
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
globals: {
crypto: {
getRandomValues: (arr) => require("crypto").randomBytes(arr.length),
},
},
};

- 2,677
- 2
- 26
- 36
I'm using vue-jest, and what worked for me is the following configuration in jest.config.js
file:
module.exports = {
...
setupFiles: [
'<rootDir>/tests/settings/jest.crypto-setup.js',
],
};
and in jest.crypto-setup.js
:
global.crypto = {
getRandomValues: (arr) => require('crypto').randomBytes(arr.length)
};
Adding the getRandomValues
function definition directly in module.exports
didn't work since the globals
object must be json-serializable (as it is specified here: https://jestjs.io/docs/configuration#globals-object).

- 61
- 1
- 3
-
I tried many solutions (using React + TS) and this is the one that worked for me. Thanks! – Ann Kilzer Aug 04 '21 at 09:14
-
1Just want to chime in and say that this worked for me as well. I use CreateReactApp which expects the file src/setupTests.js to contain these types of definitions. I added the setupTests.js file and used the exact code specified in marinona21's jest.cypto-setup.js file. – mdebeus Oct 15 '21 at 16:38
-
For the ones using jsdom
(jest-environment-jsdom
) environment with Jest >=28
you should define replacement module as a 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
}
})
I am using @peculiar/webcrypto
but other implementations should work also.

- 487
- 7
- 15
-
1this is so far in my opinion the best approach, others messing with the globalThis will work but the question is about the window object. If you don't need "@peculiar/webcrypto" you can simply do the below `Object.defineProperty(window, 'crypto', { get() { return require('crypto'); }, });` – byverdu Jul 07 '23 at 12:29
The default crypto
dependency didn't work for me during testing with Jest.
Instead I used the @peculiar/webcrypto
library:
yarn add -D @peculiar/webcrypto
Then in your Jest setup file, just add this:
import { Crypto } from "@peculiar/webcrypto";
window.crypto = new Crypto();

- 3,115
- 5
- 28
- 39
-
1This solution does not work for me, I get an error about `window.crypto` being readonly. – Mozgor Oct 12 '21 at 12:54
Add crypto
global for your jest environment as if it were in browser.
Your jest.config.js should look like:
const {defaults} = require('jest-config');
module.exports = {
globals: {
...defaults.globals,
crypto: require('crypto')
}
};

- 1,989
- 15
- 9
I have this problem in Angular 8 with Jest tests for lib that are using uuid generator. In jest test setup i mock this:
Object.defineProperty(global.self, 'crypto', {
value: {
getRandomValues: arr => arr
},
});

- 308
- 3
- 6
dspacejs's answer almost worked for me, except I had the same problem as Mozgor. I got an error saying that window.crypto is readonly. You can use Object.assign instead of directly trying to overwrite it.
Install @peculiar/webcrypto with yarn add -D @peculiar/webcrypto
or npm i --save-dev @peculiar/webcrypto
Then add the following to your Jest setup file:
import { Crypto } from "@peculiar/webcrypto";
Object.assign(window, {
crypto: new Crypto(),
})

- 103
- 1
- 9
Building upon what others suggested here, I resolved the issue with window.crypto.subtle.digest with the following:
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()
)
);
},
},
},
});
Or, if not using 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()
)
);
},
},
},
});
The reformating of the string is optional. It is also possible to hardcode the algorithm, e.g. by stating 'sha256' or 'sha512' or alike.

- 1,140
- 14
- 26
-
Thanks! This work for me for `TypeError: Cannot read properties of undefined (reading 'digest')` error – Anson Nov 07 '22 at 21:11
-
Worked well thx a lot. I just had to parse that this is missing import { createHash } from 'crypto'; – Morten May 16 '23 at 06:41
In the default configuration, Jest assumes you are testing a Node.js environment. But when you are getting errors using methods of the window
object, you are probably making a web app.
So if you are making a web app, you should use "jsdom" as your "testEnvironment". To do this, insert "testEnvironment": "jsdom",
into your Jest configurations.
If you maintain a "jest.config.js" file, then add it like:
module.exports = {
...
"testEnvironment": "jsdom",
...
};
Or if, like me, you keep the Jest configs in "package.json":
{
...,
"jest": {
...,
"testEnvironment": "jsdom",
...
},
...
}

- 7,876
- 4
- 34
- 56
-
This doesn't appear to resolve the issue, just changes the environment...? – Douglas Gaskell Aug 20 '22 at 04:19
Depency injection is one way to solve this.
Node.js provides an implementation of the standard Web Crypto API. Use require('node:crypto').webcrypto to access this module.
So you pass the crypto object to the code that depends on it.
Notice how we "inject" the correct crypto object when invoking the method 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);
});

- 487
- 3
- 5
late to the party, but I usually do something like:
// 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' });
});
});

- 107
- 11
I have implemented it using jest and it failed to execute after I upgraded jest version. Earlier I was using in this way :
global.crypto = {
getRandomValues: jest.fn();
}
After upgrade, it was failing. So I tried in this way :
global.crypto.getRandomValues = jest.fn();
and it worked fine.

- 1
- 2