I took Stuart Watt's decision and ran with it (and got a little carried away). Stuart's solution is good, but I was not inspired by the idea that the random number generator will always drop 0.5 - it seems that there have been situations where you are counting on some variance. I also wanted to laugh at crypto.randomBytes for my password salts (using Jest on the server side). I spent a little time on this, so I decided to share my knowledge.
One of the things I've noticed is that even if you have a repeating stream of numbers, introducing a new call to Math.random() can ruin all subsequent calls. I found a way around this problem. This approach should be applicable to all random things that you need to make fun of.
(note: if you want to steal this, you need to install Chance.js - yarn/npm add/install chance )
To fake Math.random , put this in one of the files your package.json array points to {"jest":{"setupFiles"} :
const Chance = require('chance') const chances = {} const mockMath = Object.create(Math) mockMath.random = (seed = 42) => { chances[seed] = chances[seed] || new Chance(seed) const chance = chances[seed] return chance.random() } global.Math = mockMath
You will notice that Math.random() now has a parameter - the seed. This seed can be a string. This means that when you write your code, you can call the random number generator that you need by name. When I added a test to the code to check if this worked, I did not put it at the beginning. This ruined my earlier mocking snapshots of Math.random() . But then, when I changed it to Math.random('mathTest') , he created a new generator called "mathTest" and stopped intercepting the sequence from the default sequence.
I also made fun of crypto.randomBytes for my password salts. So when I write code to generate my salts, I can write crypto.randomBytes(32, 'user sign up salt').toString('base64') . Thus, I can be sure that no subsequent call to crypto.randomBytes will interfere with my sequence.
If anyone else is interested in mocking crypto , here's how. Put this code inside <rootDir>/__mocks__/crypto.js :
const crypto = require.requireActual('crypto') const Chance = require('chance') const chances = {} const mockCrypto = Object.create(crypto) mockCrypto.randomBytes = (size, seed = 42, callback) => { if (typeof seed === 'function') { callback = seed seed = 42 } chances[seed] = chances[seed] || new Chance(seed) const chance = chances[seed] const randomByteArray = chance.n(chance.natural, size, { max: 255 }) const buffer = Buffer.from(randomByteArray) if (typeof callback === 'function') { callback(null, buffer) } return buffer } module.exports = mockCrypto
And then just call jest.mock('crypto') (again, I have it in one of my "setupFiles"). Since I am releasing it, I went ahead and made it compatible with the callback method (although I am not going to use it that way).
These two code fragments pass all 17 of these tests (I created __clearChances__ functions for beforeEach() - it just removes all the keys from the chances hash)
Update: I have been using this for several days, and I think it works pretty well. The only thing I think that perhaps the best strategy would be to create the Math.useSeed function, which runs at the top of the tests requiring Math.random