Wziąłem rozwiązanie Stuarta Wata i uciekłem z nim (i trochę mnie porwał). Rozwiązanie Stuarta jest dobre, ale byłem rozczarowany tym, że generator liczb losowych zawsze wypluwa 0.5 - wydaje się, że byłyby sytuacje, w których liczysz na jakąś wariancję. Chciałem również wyśmiewać się z crypto.randomBytes
dla moich soli do haseł (używając polecenia "po stronie serwera"). Spędziłem trochę czasu, więc pomyślałem, że podzielę się wiedzą.
Jedną z rzeczy, które zauważyłem jest to, że nawet jeśli masz powtarzalny strumień liczb, wprowadzenie nowego połączenia pod numer Math.random()
może zepsuć wszystkie kolejne połączenia. Znalazłem sposób obejścia tego problemu. To podejście powinno mieć zastosowanie do praktycznie każdej przypadkowej rzeczy, którą trzeba udawać.
(dygresja: jeśli chcesz ukraść to, musisz zainstalować Chance.js - yarn/npm add/install chance
)
drwić Math.random
, umieścić to w jednym z plików wskazał przez swoich package.json
„s {"jest":{"setupFiles"}
tablicy:
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
Zauważysz, że Math.random()
ma teraz parametr - nasiono. Ten materiał siewny może być ciągiem. Oznacza to, że podczas pisania kodu można wywoływać generator liczb losowych według nazwy. Kiedy dodałem test kodu, aby sprawdzić, czy to zadziałało, nie umieściłem go. Zepsuł on moje wcześniej sfilmowane migawki Math.random()
. Ale kiedy zmieniłem go na Math.random('mathTest')
, utworzył nowy generator o nazwie "mathTest" i przestał przechwytywać sekwencję z domyślnej.
Ja także wyśmiałem crypto.randomBytes
za moje sole do haseł. Kiedy piszę kod generujący moje sole, mogę napisać: crypto.randomBytes(32, 'user sign up salt').toString('base64')
. W ten sposób mogę być pewny, że żadne kolejne wywołanie do crypto.randomBytes
nie zepsuje się z moją sekwencją.
Jeśli ktoś jeszcze jest zainteresowany kpiną z crypto
w ten sposób, oto jak. Umieść ten kod wewnątrz <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
A potem po prostu zadzwonić jest.mock('crypto')
(znowu, mam go w jednym z moich „setupFiles”). Odkąd go wypuszczam, robiłem postępy zgodnie z metodą wywołania zwrotnego (chociaż nie mam zamiaru używać go w ten sposób).
Te dwa fragmenty kodu przejść wszystkie 17 these tests (stworzyłem __clearChances__
funkcje dla beforeEach()
s - to po prostu usuwa wszystkie klucze od chances
hash)
Aktualizacja: używa tego na kilka dni i teraz Myślę, że działa całkiem nieźle. Jedyną rzeczą jest to, myślę, że być może lepszą strategią byłoby stworzenie Math.useSeed
funkcji, który jest prowadzony w górnej części testów, które wymagają Math.random
Co, twoim zdaniem, "test ... ten wynik jest losowy", nawet znaczy? Odwróć monetę i możesz dostać głowy dwa razy z rzędu; jeśli nie jest to możliwe, nie jest przypadkowe. Punkt "losowy" to * nie wiesz, jakiej wartości szukasz *. W praktyce można uruchomić tę funkcję dwukrotnie dla tego samego wejścia i oczekiwać różnych wyników, ale niektóre (bardzo niskie) procent czasu, który powinien zawodzić, nawet z działającym kodem. –
Pewnie, losowość testu jest nonsensem. Pytam, jak ** przetestować ** tego rodzaju przypadki w zasadzie. Po prostu nie mam pojęcia, jak to zrobić. –
Klucz jest tutaj testem, a nie przypadkowością. Może losowa część musi zostać pominięta. Jestem noobem podczas testowania, więc poproszę o noob. –