Domanda

Sono riuscito a ottenere xUnità lavorando al mio piccolo assemblaggio campione.Adesso voglio vedere se riesco a groccare FsCheck pure.Il mio problema è che sono perplesso quando si tratta di definire le proprietà di test per le mie funzioni.

Forse semplicemente non ho un buon insieme di funzioni di esempio, ma quali sarebbero buone proprietà di test per queste funzioni, ad esempio?

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

Nessuna soluzione corretta

Altri suggerimenti

Ci sono già molte risposte specifiche, quindi proverò a dare alcune risposte generali che potrebbero darti qualche idea.

  1. Proprietà induttive per funzioni ricorsive.Per funzioni semplici, ciò equivale probabilmente a reimplementare la ricorsione.Tuttavia, mantienilo semplice:mentre l'implementazione effettiva il più delle volte si evolve (ad es.diventa ricorsivo in coda, aggiungi memoizzazione, ...) mantieni la proprietà semplice.Il combinatore di proprietà ==> di solito torna utile qui.La tua funzione di coppia potrebbe essere un buon esempio.
  2. Proprietà che detengono più funzioni in un modulo o tipo.Questo di solito è il caso quando si controllano i tipi di dati astratti.Per esempio:aggiungere un elemento a un array significa che l'array contiene quell'elemento.Questo controlla la coerenza di Array.add e Array.contains.
  3. Andata e ritorno:questo è utile per le conversioni (ad es.parsing, serializzazione) - genera una rappresentazione arbitraria, serializzala, deserializzala, verifica che sia uguale all'originale.Potresti essere in grado di farlo con splitOn e concat.
  4. Proprietà generali come controlli di integrità.Cerca proprietà generalmente note che potrebbero valere: cose come commutatività, associatività, idempotenza (applicare qualcosa due volte non cambia il risultato), riflessività, ecc.L'idea qui è più quella di esercitare un po' la funzione: vedere se fa qualcosa di veramente strano.

Come consiglio generale, cerca di non farne un problema troppo grande.Per sndBigger, una buona proprietà sarebbe:

Let `` dovrebbe restituire vero se e solo se SND è più grande`` (a: int) (b: int) = sndbigger (a, b) = b> a

E probabilmente questa è esattamente l'implementazione.Non preoccuparti: a volte un semplice test unitario vecchio stile è proprio ciò di cui hai bisogno.Nessun senso di colpa necessario!:)

Forse questo link (del team Pex) fornisce anche alcune idee.

Comincerò con sndBigger - si tratta di una funzione molto semplice, ma è possibile scrivere alcune proprietà che dovrebbero tenere su di esso. Ad esempio, cosa succede quando si invertire i valori nella tupla:

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

Modifica Questa regola prop_sndBiggerSwap non sempre in possesso (vedi commento di KVB ). Tuttavia il seguente dovrebbe essere corretta:

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

Per quanto riguarda la funzione di pairs, KVB già postato alcune buone idee. Inoltre, si potrebbe verificare che girando la lista trasformata di nuovo in un elenco di elementi restituisce la lista originale (è necessario per gestire il caso in cui la lista di input è dispari - a seconda di quale sia la funzione pairs dovrebbe fare in questo caso):

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

Per splitOn, possiamo testare cosa simile - se concatenare tutte le liste restituite, dovrebbe dare la lista originale (questo non consente di verificare il comportamento scissione, ma è una buona cosa per cominciare - almeno garanzie che non andrà persa elementi).

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

Non sono sicuro se FsCheck in grado di gestire questo però (!) Perché la proprietà prende una funzione come argomento (per cui sarebbe necessario generare "funzioni casuali"). Se questo non funziona, è necessario fornire un paio di proprietà più specifiche con qualche funzione f scritto a mano. Successivamente, l'attuazione del controllo che f restituisce vero per tutte le coppie adiacenti negli elenchi spacco (come KVB suggerisce) in realtà non è così difficile:

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

Probabilmente l'unica ultima cosa che si potrebbe verificare è che f restituisce false quando si dà l'ultimo elemento da una lista e il primo elemento nella lista successiva. Quanto segue non è del tutto completa, ma mostra la strada da percorrere:

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

L'ultimo esempio mostra anche che si dovrebbe verificare se la funzione splitOn può restituire un elenco vuoto come parte della lista restituita dei risultati (perché in tal caso, non si poteva trovare prima l'ultimo elemento /).

Per qualche codice (ad esempio sndBigger), l'applicazione è così semplice che qualsiasi proprietà sarà almeno così complesso come il codice originale, in modo test tramite FsCheck potrebbe non avere senso. Tuttavia, per le altre due funzioni qui ci sono alcune cose che si potrebbe verificare:

  • pairs
    • Cosa si aspetta quando la lunghezza originale non è divisibile per due? Si potrebbe verificare la presenza di un'eccezione se questo è il comportamento corretto.
    • List.map fst (pairs x) = evenEntries x e List.map snd (pairs x) = oddEntries x per le funzioni semplici evenEntries e oddEntries cui è possibile scrivere.
  • splitOn
    • Se ho capito la tua descrizione di come si suppone che la funzione di lavorare, allora si potrebbe verificare le condizioni del tipo "Per ogni lista nel risultato di splitOn f l, non ci sono due voci consecutive soddisfare f" e "liste Prendendo (l1,l2) da splitOn f l a due a due, f (last l1) (first l2) detiene". Purtroppo, la logica qui sarà probabilmente paragonabili per complessità alla realizzazione stessa.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top