Ich interessiere mich für Best-Practice-Techniken, wenn überhaupt, für Unit-Test-Funktionen, die Zufälligkeit verwenden. Um klar zu sein, bin ich nicht besorgt mit dem Test der Verteilung von Zufallszahlengeneratoren.Wie testen wir Funktionen, die Zufälligkeit verwenden?
Als Spielzeug Beispiel nehmen wir diese Funktion betrachten:
// Returns a random element from @array. @array may not be empty.
int GetRandomElement(int[] array);
Antworten auf this question legen nahe, dass wir ein Mock Zufallsquelle injizieren kann, was Sinn macht. Aber ich bin mir nicht sicher, wie ich den Mock benutzen soll. Zum Beispiel, nehmen wir an, dass wir diese Schnittstelle:
// A mock-friendly source of randomness.
interface RandomnessSource {
// Returns a random int between @min (inclusive) and @max (exclusive).
int RandomInt(int min, int max);
}
... Und die Unterschrift von GetRandomElement()
Um dies zu ändern:
// Returns a random element from @array, chosen with @randomness_source.
// @array may not be empty.
int GetRandomElement(int[] array, RandomnessSource randomness_source);
In Ordnung, jetzt ein Test aussehen könnte:
MockRandomnessSource mock = new MockRandomnessSource();
mock.ExpectCall(RandomnessSource::RandomInt(0, 5)).Return(2);
AssertEquals(GetRandomElement({0, 10, 20, 30, 40}, mock), 20);
... was in Ordnung könnte funktionieren, aber nur, wenn die Umsetzung wie folgt aussieht:
... Aber nichts in der Funktion Spezifikation verhindert eine Implementierung wie folgt aus:
// Less intuitive, but still a conforming implementation.
int GetRandomElement(int[] array, RandomnessSource randomness_source) {
// Pick a random number between [1..n+1), only to subtract 1 from it.
return array.Get(randomness_source.RandomInt(1, array.Length() + 1) - 1);
}
Eine Idee, die in den Sinn springt ist, dass wir weiter die Funktion des Vertrages beschränken kann, wie folgt aus:
// Returns a random element from @array, chosen with @randomness_source by
// by calling @RandomnessSource::RandomInt to pick a random index between
// 0 and the length of @array.
int GetRandomElement(int[] array, RandomnessSource randomness_source);
... Aber ich kann den Eindruck nicht bekommen, dass dies den Funktionsvertrag zu sehr einschränkt.
Ich vermute auch, dass es bessere Möglichkeiten geben könnte, die Schnittstelle RandomnessSource
zu definieren, um seine Anrufer zugänglicher für Komponententests zu machen, aber ich bin nicht ganz sicher, was/wie.
... Was mich auf die Frage bringt: Was sind die Best-Practice-Techniken (wenn überhaupt) für Komponententestfunktionen, die Zufälligkeit verwenden?
Es stellt sich heraus, dass "zufällig" schwer zu definieren ist. Die Frage wird also: "Warum brauchst du 'zufällig'?" Um beispielsweise GetRandomElement zu testen, benötigen Sie definitiv keine zufällige Quelle. –
@JamesKPolk Könnten Sie das näher erläutern? Können Sie ein Beispiel für das Testen von GetRandomElement() geben? –
Die Testversion einer 'RandomnessSource' sollte Werte in den Extrembereichen ihres Bereichs und dann etwas in der Mitte zurückgeben. Sie können entscheiden, wie viel Testabdeckung Sie haben möchten. Sie könnten sogar eine deterministische, aber "zufällige" Suchsequenz verwenden, z.Einbinden von etwas wie der java.util.Random-Klasse (wenn Sie Java verwenden) mit einem festen Seed. –