Frage

Ich habe es geschafft zu bekommen xUnit auf meine kleine Probe Arbeits Montage. Jetzt möchte ich sehen, ob ich grok kann FsCheck zu. Mein Problem ist, dass ich ratlos bin, wenn es um die Definition Testeigenschaften für meine Funktionen kommt.

Vielleicht habe ich einfach nicht eine gute Probe Satz von Funktionen bekam, aber was für diese Funktionen gute Testeigenschaften wäre zum Beispiel?

//transforms [1;2;3;4] into [(1,2);(3,4)]
pairs : 'a list -> ('a * 'a) list      //'

//splits list into list of lists when predicate returns 
//  true for adjacent elements
splitOn : ('a -> 'a -> bool) -> 'a list -> 'a list list

//returns true if snd is bigger
sndBigger : ('a * 'a) -> bool (requires comparison)

Keine korrekte Lösung

Andere Tipps

Es gibt bereits viele konkrete Antworten, so werde ich versuchen, einige allgemeine Antworten zu geben, die Ihnen ein paar Ideen könnten.

  1. Induktive Eigenschaften für rekursive Funktionen. Für einfache Funktionen ergibt dies wahrscheinlich zu einer Neuimplementierung der Rekursion. Aber halten Sie es einfach: während die tatsächliche Umsetzung häufiger als nicht sich entwickelt (zum Beispiel wird es Schwanz-rekursiv, die Sie hinzufügen memoization, ...) halten die Eigenschaft einfach. Die ==> Eigenschaft combinator kommt in der Regel in praktisch hier. Ihre Paare Funktion könnte ein gutes Beispiel machen.
  2. Eigenschaften, die in einem Modul oder Typ über mehrere Funktionen halten. Dies ist in der Regel der Fall, wenn abstrakte Datentypen zu überprüfen. Zum Beispiel: Hinzufügen eines Elements zu einem Array bedeutet, dass das Array, das Element enthält. Diese prüft die Konsistenz der Array.add und Array.contains.
  3. Rundreisen: Das ist gut für die Conversions (z Parsing, Serialisierung) - erzeugen eine beliebige Darstellung, serialisiert es, deserialisieren es, zu prüfen, ob es das Original entspricht. Sie können dies tun können, mit splitOn und concat.
  4. Allgemeine Eigenschaften als Plausibilitätsprüfungen. Suchen Sie nach allgemein bekannten Eigenschaften, die halten kann - Dinge wie Kommutativität, Assoziativität, idempotence (Anwendung etwas zweimal das Ergebnis nicht ändern), Reflexivität, etc. Die Idee hier ist die Funktion ein bisschen zu trainieren - nachsehen, ob es irgendetwas tut wirklich seltsam .

Als allgemeiner Ratschlag, versuchen Sie nicht zu große Sache daraus zu machen. Für sndBigger, wäre eine gute Eigenschaft sein:

let `` sollte return true, wenn und nur wenn snd ist bigger`` (a: int) (b: int) =     sndBigger (a, b) = b> a

Und das ist wahrscheinlich genau die Umsetzung. Kümmere dich nicht um es - manchmal eine einfache, altmodische Unit-Test ist genau das, was Sie brauchen. Keine Schuld notwendig! :)

Vielleicht Link (von dem Pex Team) auch gibt einige Ideen.

Ich werde mit sndBigger beginnen - es ist eine sehr einfache Funktion ist, aber Sie können einige Eigenschaften schreiben, die über sie halten sollen. Zum Beispiel, was passiert, wenn man die Werte im Tupel umkehren:

// Reversing values of the tuple negates the result
let swap (a, b) = (b, a)
let prop_sndBiggerSwap x = 
  sndBigger x = not (sndBigger (swap x))

// If two elements of the tuple are same, it should give 'false'
let prop_sndBiggerEq a = 
  sndBigger (a, a) = false

EDIT: Diese Regel prop_sndBiggerSwap nicht immer halten (siehe Kommentar von kvb ). Allerdings sollte die folgende sein korrekt:

// Reversing values of the tuple negates the result
let prop_sndBiggerSwap a b = 
  if a <> b then 
    let x = (a, b)
    sndBigger x = not (sndBigger (swap x))

Im Hinblick auf die pairs Funktion kvb bereits einige gute Ideen geschrieben. Darüber hinaus können Sie überprüfen, ob die transformierte Liste wieder in eine Liste von Elementen Drehen der ursprünglichen Liste zurück (Sie werden den Fall behandeln müssen, wenn die Eingangsliste ungerade ist - je nachdem, was die pairs Funktion in diesem Fall tun sollten):

let prop_pairsEq (x:_ list) = 
  if (x.Length%2 = 0) then
    x |> pairs |> List.collect (fun (a, b) -> [a; b]) = x
  else true

Für splitOn können wir ähnliche Sache testen - wenn Sie alle zurückgegebenen Listen verketten, sollte es die ursprüngliche Liste geben (dies ist nicht das Spaltverhalten nicht überprüfen, aber es ist eine gute Sache, mit zu beginnen - es zumindest Garantien dass es werden keine Elemente verloren).

let prop_splitOnEq f x = 
  x |> splitOn f |> List.concat = x

Ich bin mir nicht sicher, ob FsCheck kann damit umgehen wenn (!), Da die Eigenschaft eine Funktion als Argument übernimmt (so würde es brauchen „Zufallsfunktionen“ zu erzeugen). Wenn dies nicht funktioniert, müssen Sie mit einiger handgeschriebenen Funktion f ein paar speziellere Eigenschaften bereitzustellen. Als nächstes wird die Überprüfung, dass f kehrt gilt für alle benachbarten Paare in den geteilten Listen Implementierung (wie kvb schon sagt) ist eigentlich nicht so schwierig:

let prop_splitOnAdjacentTrue f x = 
  x |> splitOn f 
    |> List.forall (fun l -> 
         l |> Seq.pairwise 
           |> Seq.forall (fun (a, b) -> f a b))

Wahrscheinlich das einzige letzte, was Sie prüfen könnte, ist, dass f kehrt false, wenn Sie es das letzte Element aus einer Liste geben und das erste Element aus der nächsten Liste. Im Folgenden ist nicht vollständig abgeschlossen, aber es zeigt den Weg zu gehen:

let prop_splitOnOtherFalse f x = 
  x |> splitOn f
    |> Seq.pairwise 
    |> Seq.forall (fun (a, b) -> lastElement a = firstElement b)

Das letzte Beispiel zeigt auch, dass Sie sollten prüfen, ob die splitOn Funktion eine leere Liste als Teil der zurückgegebenen Liste der Ergebnisse zurückgeben kann (weil in diesem Fall, Sie nicht erste / letzte Element finden konnte).

Für einige Codes (z sndBigger), ist die Implementierung so einfach, dass jede Eigenschaft mindestens so komplex wie der Original-Code sein, so über FsCheck Prüfung kann nicht sinnvoll. Doch für die beiden anderen hier Funktionen einige Dinge, die Sie könnten überprüfen:

  • pairs
    • Was erwartet wird, wenn die ursprüngliche Länge durch zwei teilbar ist? Sie könnten zum Auslösen einer Ausnahme überprüfen, ob das ist das richtige Verhalten.
    • List.map fst (pairs x) = evenEntries x und List.map snd (pairs x) = oddEntries x für einfache Funktionen evenEntries und oddEntries, die Sie schreiben können.
  • splitOn
    • Wenn ich verstehe Ihre Beschreibung, wie die Funktion funktionieren soll, dann könnte man Bedingungen prüfen, wie „Für jede Liste im Ergebnis der splitOn f l, keine zwei aufeinander folgenden Einträge erfüllen f“ und „Taking Listen (l1,l2) von splitOn f l paarweise, f (last l1) (first l2) hält“. Leider wird die Logik hier wahrscheinlich vergleichbar sein in der Komplexität der Implementierung selbst.
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top