web-dev-qa-db-ja.com

Jestを使用して出力がランダムな関数をテストする方法は?

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'
  },
}
_
26

これが私がテストファイルの先頭に置いたものです。

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自体の内部を改ざんしないようにするための、追加のもう少しの注意です。

42
Stuart Watt

私が使用した:

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

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

テスト以外の機能の追加と復元は簡単です。

10
Rui Fonseca

私はスチュアートワットの解決策を取り、それを実行しました(そして少し夢中になりました)。スチュアートの解決策は良いですが、乱数ジェネレーターが常に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_関数を作成することだと思います

8
Tim

次の質問を自問します。

  • 本当にランダム出力をテストする必要がありますか?必要な場合は、範囲をテストするか、値自体ではなく有効な形式で数値を受け取ったことを確認します
  • cの値をテストするだけで十分ですか?

ランダム値の生成をMockにカプセル化し、既知の値のみを返すようにテストで生成をオーバーライドする方法がある場合があります。これは私のコードでは一般的な方法です。 new Date()のようなコンストラクターをモックする方法jestjsでの同様のアプローチのように聞こえます。

5
cringe

いつでも jest-mock-random を使用できます

ただし、最初の回答で提案されているモックよりも少し多くの機能を提供します。

たとえば、testmockRandomWith(0.6);の前に使用でき、テストでのMath.randomは常にこの予測可能な値を返します

1
SirPeople

リテラルランダムデータのモックは、テストの方法とは異なります。 「出力がランダムである関数をテストする方法」では効果的なランダム性を確保するために出力の統計分析を行う必要があるため、述べられているように、質問は少し広いです-これは疑似の作成者によって行われた可能性があります乱数ジェネレータ。

代わりに「その出力はランダム」と推測すると、ランダムデータに関係なく関数が適切に機能することを確認し、Math.random呼び出しをモックして、特定の基準(分散をカバーする)を満たす数値を返すだけで十分です。その関数はサードパーティの境界であり、テストが必要ですが、私の推論に基づいてテストされているものではありません。そうでない場合-その場合、上記の段落を参照してください。

0
Richard Barker