如何使用Jest测试输出是随机的函数?

时间:2017-01-10 13:40:15

标签: javascript jestjs

如何使用Jest测试输出是随机的函数?像这样:

$db = $this->Bookcopy->getDataSource();
$value = 'AVAILABLE';
$value = $db->value($value, 'String');

$this->Bookcopy->updateAll(['status' => $value],['Bookcopy.status'=>'REQUESTED'])

{data: "Borrow", status: "success"}
data:"Borrow"
status:"success"

因此import cuid from 'cuid'; const functionToTest = (value) => ({ [cuid()]: { a: Math.random(), b: new Date().toString(), c: value, } }); 的输出将类似于:

functionToTest('Some predictable value')

6 个答案:

答案 0 :(得分:29)

这是我放在测试文件顶部的内容:

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

在从该文件运行的测试中,Math.random始终返回0.5

对于这个想法应该完全赞同:https://stackoverflow.com/a/40460395/2140998,它澄清了这种覆盖是特定于测试的。我的Object.create只是额外的一点点谨慎,避免篡改Math本身的内部。

答案 1 :(得分:4)

我问自己以下问题:

  • 我真的需要测试随机输出吗?如果必须,我很可能测试范围或确保我收到的是有效格式的数字,而不是价值本身
  • 是否足以测试c
  • 的值

有时候有一种方法可以将随机值的生成封装在 Mock 中,并覆盖测试中的生成以仅返回已知值。这是我的代码中的常见做法。 How to mock a constructor like new Date()听起来像 jestjs 中的类似方法。

答案 2 :(得分:4)

我采取了Stuart Watt的解决方案,然后继续使用它(并且有点被带走了)。斯图尔特的解决方案很好但我对于随机数生成器总是吐出0.5的想法感到不知所措 - 似乎有些情况下你会指望一些差异。我还想模拟crypto.randomBytes我的密码盐(使用Jest服务器端)。我花了一点时间在这上面,所以我想我会分享知识。

我注意到的一件事是,即使你有一个可重复的数字流,引入一个新的Math.random()调用可能会搞砸所有后续调用。我找到了解决这个问题的方法。这种方法应该适用于你需要模拟的任何随机事物。

(旁注:如果你想偷这个,你需要安装Chance.js - yarn/npm add/install chance

要模拟Math.random,请将其放在package.json的{​​{1}}数组指向的其中一个文件中:

{"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 现在有一个参数 - 种子。这个种子可以是一个字符串。这意味着,当您编写代码时,可以通过名称调用所需的随机数生成器。当我在代码中添加一个测试以检查这是否有效时,我没有给它种下种子。它搞砸了我之前模拟的Math.random()快照。但是当我将其更改为Math.random()时,它创建了一个名为“mathTest”的新生成器,并停止拦截默认序列。

我还嘲笑Math.random('mathTest')我的密码盐。因此,当我编写代码来生成我的盐时,我可能会写crypto.randomBytes。这样我就可以非常肯定,对crypto.randomBytes(32, 'user sign up salt').toString('base64')的后续调用不会导致我的序列混乱。

如果有其他人有兴趣以这种方式嘲笑crypto.randomBytes,那么就是这样的。将此代码放在crypto

<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 (再次,我在其中一个“setupFiles”中)。因为我正在发布它,所以我继续使它与回调方法兼容(虽然我无意以这种方式使用它)。

这两段代码传递了these tests的所有17个(我为jest.mock('crypto')创建了__clearChances__函数 - 它只删除beforeEach()哈希中的所有键

更新:现在已经使用了几天,我觉得它运作得很好。唯一的问题是我认为也许更好的策略是创建一个chances函数,该函数运行在需要Math.useSeed的测试的顶部

答案 3 :(得分:4)

我用过:

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

afterEach(() => {
    global.Math.random.mockRestore();
})

在测试之外轻松添加和恢复功能。

答案 4 :(得分:0)

您可以随时使用jest-mock-random

但它提供的功能比第一个答案中提出的嘲弄功能要多一些。

例如,您可以在testmockRandomWith(0.6);之前使用,并且测试中的Math.random将始终返回此可预测值

答案 5 :(得分:0)

模拟文字随机数据并不是测试的确切方法。如前所述,这个问题有点笼统,因为“如何测试输出是随机的函数”要求您对输出进行统计分析,以确保有效的随机性-这很可能是由伪造物的创建者完成的。随机数生成器。

相反,推断“该输出是随机的”意味着您想要确保函数正常运行,而与随机数据无关,然后仅对Math.random调用进行模拟以返回满足您的特定条件的数字(覆盖任何方差)就足够了。该功能是第三方边界,在需要测试时,并不是根据我的推断进行测试。除非是-在这种情况下,请参阅上面的段落。