How to check a function that is randomly output using Jest?

How to check a function that is randomly output using Jest? Like this:

import cuid from 'cuid'; const functionToTest = (value) => ({ [cuid()]: { a: Math.random(), b: new Date().toString(), c: value, } }); 

So the output of functionToTest('Some predictable value') would be something like this:

 { 'cixrchnp60000vhidc9qvd10p': { a: 0.08715126430943698, b: 'Tue Jan 10 2017 15:20:58 GMT+0200 (EET)', c: 'Some predictable value' }, } 
+16
source share
6 answers

Here is what I put at the top of the test file:

 const mockMath = Object.create(global.Math); mockMath.random = () => 0.5; global.Math = mockMath; 

In tests run from this file, Math.random always returns 0.5 .

Full credit should go for this in theory: fooobar.com/questions/314364 / ... , which states that this rewriting is specific to testing. My Object.create is just an extra extra precaution to avoid interference from Math itself.

+33
source

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

+6
source

I asked myself the following questions:

  • Do I need to check for random output? If I need to, I would most likely check the ranges or make sure I got the number in a valid format, not the value itself
  • Is it enough to check the value for c ?

Sometimes there is a way to encapsulate the generation of a random value in the Mock and override the generation in your test to return only known values. This is a common practice in my code. How to mock a constructor similar to the new Date () seems like a similar approach in jestjs.

+4
source

You can always use jest-mock-random

But it offers a little more functionality than mockery, as suggested in the first answer.

For example, you can use before testmockRandomWith(0.6); and your Math.random in the test will always return this predicted value

+1
source

I used:

 beforeEach(() => { jest.spyOn(global.Math, 'random').mockReturnValue(0.123456789); }); afterEach(() => { global.Math.random.mockRestore(); }) 

Easy to add and restore functionality outside of tests.

+1
source

Mocking literal random data is not quite a way to check. The question, as was said, is a bit broader because “how to check a function whose output is random” requires you to statistically analyze the output in order to ensure effective randomness — which was probably done by the creators of your pseudo random number generator.

Instead, it is understood that “output is random” means that you want the function to function correctly regardless of random data, then just laugh at the Math.random call to get numbers that meet your specific criteria (covering any variance). This function is a third-party border, which, despite the need for testing, is not what is being tested based on my conclusion. If this is not the case, then refer to the paragraph above.

0
source

Source: https://habr.com/ru/post/1262577/


All Articles