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

ki0zmccv  于 2023-02-27  发布在  Jest
关注(0)|答案(7)|浏览(177)

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

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

因此functionToTest('Some predictable value')的输出如下所示:

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

l5tcr1uw1#

我使用了:

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

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

很容易在测试之外添加和恢复功能。

lyfkaqu1

lyfkaqu12#

下面是我在测试文件顶部的内容:

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本身的内部。

llmtgqce

llmtgqce3#

我采用了斯图尔特·瓦特的解决方案(有点忘乎所以)。Stuart的解决方案很好,但我对随机数生成器总是输出0.5的想法不感兴趣--似乎有些情况下你需要依赖一些方差。我还想模拟crypto.randomBytes作为我的密码salts(使用Jest服务器端)。我花了一些时间在这上面,所以我想我应该分享一下知识。
我注意到的一件事是,即使你有一个可重复的数字流,引入一个对Math.random()的新调用可能会搞砸所有后续调用。我发现了一个解决这个问题的方法。这个方法应该适用于几乎任何你需要模拟的随机事物。
(side注意:如果你想窃取这个,你需要安装Chance.js-yarn/npm add/install chance
要模拟Math.random,请将以下代码放入package.json{"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()现在有了一个参数--seed,这个seed可以是一个字符串,这意味着当你写代码的时候,你可以通过名字来调用你想要的随机数生成器,当我在代码中添加了一个测试来检查它是否工作的时候,我没有把种子放进去。它搞砸了我以前嘲笑的Math.random()快照。但是当我把它改为Math.random('mathTest')时,它创建了一个名为“mathTest”的新生成器,并停止从默认生成器截取序列。
我还模拟了crypto.randomBytes作为我的密码salts,所以当我编写生成salts的代码时,我可能会写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

然后调用jest.mock('crypto')(同样,我在我的一个“setupFiles”中有它),因为我发布了它,所以我继续使它与回调方法兼容(尽管我不打算那样使用它)。
这两段代码传递了these tests的所有17个键(我为beforeEach()创建了__clearChances__函数-它只是删除了chances散列中的所有键)
更新:我已经用了几天了,我认为它工作得很好,唯一的问题是我认为也许更好的策略是创建一个Math.useSeed函数,在需要Math.random的测试的顶部运行

db2dz4w8

db2dz4w84#

我会问自己以下几个问题:

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

有时候有一种方法可以将随机值的生成封装在 Mock 中,然后在测试中覆盖生成,只返回已知值。这是我代码中的一种常见做法。如何模拟new Date()这样的构造函数听起来与 jestjs 中的方法类似。

xe55xuns

xe55xuns5#

您始终可以使用jest-mock-random
但它提供了比第一个答案中提出的嘲笑它多一点的功能。
例如,您可以在testmockRandomWith(0.6);之前使用,并且您在test中的Math.random将始终返回此可预测值

fjaof16o

fjaof16o6#

模拟字面上的随机数据并不是正确的测试方法,正如上面所说,这个问题有点宽泛,因为“如何测试输出是随机的函数”需要你对输出进行统计分析,以确保有效的随机性--这很可能是伪随机数生成器的创建者所做的。
相反,推断“输出是随机的”意味着您希望确保函数正常运行,而不管随机数据如何,然后只是模拟Math.random调用以返回满足特定条件的数字(覆盖任何变化)就足够了。该函数是第三方边界,虽然需要测试,并不是基于我的推断所测试的,除非它是--在这种情况下,请参考上面的段落。

b09cbbtk

b09cbbtk7#

我同意理查德巴克的回答。奇怪的是,在写他的回答时有0票,而赞成票和接受的完全错过了重点。我猜这个线程太老了。

**如何使用Jest测试一个随机输出的函数?**这不是一个Jest问题,甚至不是一个编程问题,而是一个统计学(甚至哲学)问题。据我所知,数学只能最终证明一个序列是非随机的,这意味着它有一个可识别的模式。Math.random()和所有其他伪随机数生成器一样,最终都会产生一个模式,因为它们是算法。

最好的可行建议是畏缩的回答。测试返回范围并键入。所有其他建议都消除了应该测试的输出随机性,使“随机性测试”毫无意义。

相关问题