2017-12-25 19 views
0

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?

+0

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. –

+0

@JamesKPolk Könnten Sie das näher erläutern? Können Sie ein Beispiel für das Testen von GetRandomElement() geben? –

+0

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. –

Antwort

0

Normalerweise möchten Sie nicht Unit-Test "Zufälligkeit" testen. Komponententests dienen hauptsächlich dazu, festzustellen, ob eine erwartete Eingabe eine erwartete Ausgabe liefert. Sehr definiert und strukturiert. Sie möchten, dass Ihre Komponententests sehr vorhersehbar sind und sich bei jedem Test auf die gleiche Weise verhalten.

Wenn Sie es wirklich testen wollen, wird die Frage dann, wie genau wollen Sie es testen? Was passiert, wenn zwischen zwei Tests das Ergebnis anders ist? Wie definieren Sie einen erfolgreichen Test im Vergleich zu einem fehlgeschlagenen Test?

Diese Fragen sind bei normalen Komponententests, bei denen das Ergebnis sehr klar und gut definiert ist, noch offensichtlicher.

+0

Um Ihre Fragen zu beantworten: Ja, ich möchte, dass Komponententests deterministisch sind. Genauer gesagt möchte ich Funktionen testen, deren Ergebnisse normalerweise von Quellen der Zufälligkeit abhängen, indem solche Quellen durch deterministische ersetzt werden, ähnlich wie ich Funktionen testen würde, deren Ergebnisse normalerweise von Zeitintervallen abhängen, indem Zeitquellen durch Scheinuhren ersetzt werden. Wenn ein Ergebnis zwischen zwei Tests unterschiedlich ist, möchte ich den Test neu schreiben, um Nicht-Determinismus zu beseitigen. Pass/Fail-Unterscheidungen sollten mit dem übereinstimmen, was in der umgebenden Test-Harness angegeben ist (z. B. in JUnit, um ohne Ausnahme-Ausnahmen zurückzukehren). –

+0

@JaeHeonLee Also schreibe diese Tests. Nicht sicher, wie dies Ihre Frage nicht beantwortet. –

+0

Ich habe Schwierigkeiten, mir eine Möglichkeit vorzustellen, Quellen der Zufälligkeit zu verspotten, so dass der Komponententest den Vertrag und nicht die Details der Implementierung überprüft. Das heißt, ich möchte einen Test schreiben, der nicht neu geschrieben werden muss, selbst wenn der Code für GetRandomElement() vom "ziemlich intuitiven Implementierungs" -Snippet in die "weniger intuitive, aber dennoch konforme Implementierung" umgewandelt wird. Ausschnitt im ursprünglichen Post. –

Verwandte Themen