Question

J'ai réussi à obtenir xUnit travailler sur mon montage petit échantillon. Maintenant, je veux voir si je peux grok FsCheck aussi. Mon problème est que je suis perplexe en ce qui concerne la définition des propriétés de test pour mes fonctions.

Peut-être que je suis tout simplement pas eu un bon ensemble de l'échantillon de fonctions, mais ce serait de bonnes propriétés de test pour ces fonctions, par exemple?

//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)

Pas de solution correcte

Autres conseils

Il y a déjà beaucoup de réponses spécifiques, donc je vais essayer de donner quelques réponses générales qui pourraient vous donner des idées.

  1. propriétés inductifs pour les fonctions récursives. Pour des fonctions simples, cela revient probablement à nouveau la mise en œuvre de la récursivité. Cependant, restez simple: alors que la mise en œuvre réelle plus souvent évolue (par exemple, il devient récursive, vous ajoutez memoization, ...) garder la propriété directe. Le ==> propriété Combinator est généralement utile ici. Votre fonction paires pourrait faire un bon exemple.
  2. Propriétés qui détiennent sur plusieurs fonctions dans un module ou le type. Ceci est généralement le cas lors de la vérification des types de données abstraites. Par exemple: l'ajout d'un élément à un moyen de réseau que le tableau contient cet élément. Cela permet de vérifier la cohérence des Array.add et Array.contains.
  3. allers-retours: ce qui est bon pour les conversions (par exemple l'analyse syntaxique, sérialisation) - générer une représentation arbitraire, sérialiser, désérialiser, vérifiez qu'il est égal à l'original. Vous pourriez être en mesure de le faire avec splitOn et concat.
  4. Propriétés générales que les chèques de santé mentale. Recherchez les propriétés généralement connues qui peuvent contenir - des choses comme commutativité, associativité, idempotence (appliquer quelque chose ne change pas deux fois le résultat), réflexivité, etc. L'idée est ici plus d'exercer la fonction un peu - voir si elle fait quelque chose de vraiment bizarre .

En tant que pièce générale de conseils, essayez de ne pas faire trop grosse affaire de celui-ci. Pour sndBigger, une bonne propriété serait:

let `` doit retourner vrai si et seulement si SND est bigger`` (a: int) (b: int) =     sndBigger (a, b) = b> a

Et c'est probablement exactement la mise en œuvre. Ne vous inquiétez pas à ce sujet - parfois une unité simple, ancienne test est juste ce dont vous avez besoin. Pas de culpabilité nécessaire! :)

Peut-être ce lien (par l'équipe Pex) aussi donne quelques idées.

Je vais commencer par sndBigger - c'est une fonction très simple, mais vous pouvez écrire des propriétés qui devraient tenir à ce sujet. Par exemple, ce qui se passe lorsque vous inversez les valeurs du tuple:

// 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: Cette règle prop_sndBiggerSwap ne tient pas toujours (voir le commentaire par KVB ). Toutefois, le suivant doit être correct:

// 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))

En ce qui concerne la fonction pairs, KVB déjà posté quelques bonnes idées. De plus, vous pouvez vérifier que tourner la liste retransformés en une liste d'éléments retourne la liste originale (vous devrez gérer le cas où la liste d'entrée est impair - en fonction de ce que la fonction pairs doit faire dans ce cas):

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

Pour splitOn, nous pouvons tester même chose - si vous concaténer toutes les listes de retour, il doit donner la liste initiale (cela ne vérifie pas le comportement de division, mais il est une bonne chose pour commencer - au moins garanties qu'aucun élément seront perdus).

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

Je ne sais pas si FsCheck peut gérer ce bien (!) Parce que la propriété prend une fonction comme argument (il donc besoin de générer des « fonctions aléatoires »). Si cela ne fonctionne pas, vous devrez fournir quelques propriétés plus spécifiques avec certains f fonction manuscrite. Ensuite, la mise en œuvre du contrôle que f retourne vrai pour toutes les paires adjacentes dans les listes dédoublées (comme KVB suggère) n'est pas réellement si difficile:

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

Probablement la seule dernière chose que vous pouvez vérifier est que le rendement f false lorsque vous donnez le dernier élément d'une liste et le premier élément de la liste suivante. Ce qui suit est pas complètement terminée, mais il montre la voie à suivre:

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

Le dernier échantillon montre également que vous devriez vérifier si la fonction splitOn peut retourner une liste vide dans le cadre de la liste retournée des résultats (parce que dans ce cas, vous ne trouvez pas le premier / dernier élément).

Pour un code (par exemple sndBigger), la mise en œuvre est si simple que toute propriété sera au moins aussi complexe que le code d'origine, afin de tester via FsCheck ne peut pas donner un sens. Cependant, pour les deux autres fonctions, voici quelques choses que vous pouvez vérifier:

  • pairs
    • Ce qui est prévu lorsque la longueur d'origine est non divisible par deux? Vous pouvez vérifier lancer une exception si c'est le comportement correct.
    • List.map fst (pairs x) = evenEntries x et List.map snd (pairs x) = oddEntries x pour de simples fonctions evenEntries et oddEntries que vous pouvez écrire.
  • splitOn
    • Si je comprends bien votre description de la façon dont la fonction est censé fonctionner, alors vous pouvez vérifier les conditions telles que « Pour chaque liste dans le résultat de splitOn f l, pas deux entrées consécutives satisfont f » et « Prendre des listes (l1,l2) de splitOn f l par paires, f (last l1) (first l2) détient ». Malheureusement, la logique ici sera probablement comparable à la complexité de la mise en œuvre elle-même.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top