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'
},
}
_
これが私がテストファイルの先頭に置いたものです。
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
自体の内部を改ざんしないようにするための、追加のもう少しの注意です。
私が使用した:
beforeEach(() => {
jest.spyOn(global.Math, 'random').mockReturnValue(0.123456789);
});
afterEach(() => {
global.Math.random.mockRestore();
})
テスト以外の機能の追加と復元は簡単です。
私はスチュアートワットの解決策を取り、それを実行しました(そして少し夢中になりました)。スチュアートの解決策は良いですが、乱数ジェネレーターが常に0.5を吐き出すという考えに圧倒されました-いくつかの差異を当てにしている状況があるようです。私はまた、(Jestサーバー側を使用して)パスワードソルトの_crypto.randomBytes
_をモックしたかったのです。私はこれに少し時間を費やしたので、知識を共有したいと思いました。
私が気付いたことの1つは、繰り返し可能な数値のストリームがある場合でも、Math.random()
への新しい呼び出しを導入すると、後続のすべての呼び出しが台無しになる可能性があることです。この問題を回避する方法を見つけました。このアプローチは、モックする必要のあるほとんどすべてのランダムなものに適用できます。
(補足:これを盗む場合は、インストールする必要があります Chance.js -_yarn/npm add/install chance
_)
_Math.random
_をモックするには、これを_package.json
_の_{"jest":{"setupFiles"}
_配列が指すファイルの1つに配置します。
_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()
スナップショットを台無しにしました。しかし、それをMath.random('mathTest')
に変更すると、「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
_
次に、jest.mock('crypto')
を呼び出します(これも、「setupFiles」の1つにあります)。私はそれをリリースしているので、先に進み、コールバックメソッドと互換性を持たせました(そのように使用するつもりはありません)。
これらの2つのコードは、17個すべての これらのテスト を渡します(beforeEach()
sの___clearChances__
_関数を作成しました-chances
ハッシュからすべてのキーを削除するだけです)
更新:これを数日間使用していて、かなりうまくいくと思います。唯一のことは、おそらくより良い戦略は、_Math.useSeed
_を必要とするテストの上部で実行される_Math.random
_関数を作成することだと思います
次の質問を自問します。
c
の値をテストするだけで十分ですか?ランダム値の生成をMockにカプセル化し、既知の値のみを返すようにテストで生成をオーバーライドする方法がある場合があります。これは私のコードでは一般的な方法です。 new Date()のようなコンストラクターをモックする方法jestjsでの同様のアプローチのように聞こえます。
いつでも jest-mock-random を使用できます
ただし、最初の回答で提案されているモックよりも少し多くの機能を提供します。
たとえば、testmockRandomWith(0.6);
の前に使用でき、テストでのMath.randomは常にこの予測可能な値を返します
リテラルランダムデータのモックは、テストの方法とは異なります。 「出力がランダムである関数をテストする方法」では効果的なランダム性を確保するために出力の統計分析を行う必要があるため、述べられているように、質問は少し広いです-これは疑似の作成者によって行われた可能性があります乱数ジェネレータ。
代わりに「その出力はランダム」と推測すると、ランダムデータに関係なく関数が適切に機能することを確認し、Math.random呼び出しをモックして、特定の基準(分散をカバーする)を満たす数値を返すだけで十分です。その関数はサードパーティの境界であり、テストが必要ですが、私の推論に基づいてテストされているものではありません。そうでない場合-その場合、上記の段落を参照してください。