Pergunta

Eu consegui conseguir xunit trabalhando na minha pequena montagem de amostras. Agora eu quero ver se consigo entender Fscheck também. Meu problema é que estou perplexo quando se trata de definir propriedades de teste para minhas funções.

Talvez eu não tenha um bom conjunto de funções de amostra, mas quais seriam boas propriedades de teste para essas funções, por exemplo?

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

Nenhuma solução correta

Outras dicas

Já existem muitas respostas específicas, então tentarei dar algumas respostas gerais que podem lhe dar algumas idéias.

  1. Propriedades indutivas para funções recursivas. Para funções simples, isso provavelmente é para reimplementar a recursão. No entanto, mantenha-o simples: enquanto a implementação real com mais frequência evolui (por exemplo, ela se torna recursiva, você adiciona memórias, ...) Mantenha a propriedade direta. O Combinador de propriedades ==> geralmente é útil aqui. Sua função de pares pode dar um bom exemplo.
  2. Propriedades que mantêm várias funções em um módulo ou tipo. Geralmente, esse é o caso ao verificar tipos de dados abstratos. Por exemplo: adicionar um elemento a uma matriz significa que a matriz contém esse elemento. Isso verifica a consistência do Array.add e Array.Contains.
  3. Viagens redondas: isso é bom para conversões (por exemplo, análise, serialização) - gera uma representação arbitrária, serializam -a, a desesteram, verifique se é igual ao original. Você pode fazer isso com Spliton e Concat.
  4. Propriedades gerais como verificações de sanidade. Procure propriedades geralmente conhecidas que possam realizar - coisas como transferência, associatividade, idempotência (aplicar algo duas vezes não muda o resultado), reflexividade etc. A idéia aqui é mais para exercer a função um pouco - veja se faz algo realmente estranho .

Como um conselho geral, tente não fazer um acordo muito grande com isso. Para Snndbigger, uma boa propriedade seria:

Vamos `` deve retornar verdadeiro se e somente se SND for maior`` (a: int) (b: int) = sndbigger (a, b) = b> a

E isso provavelmente é exatamente a implementação. Não se preocupe com isso - às vezes um teste de unidade antiquado e simples é exatamente o que você precisa. Sem culpa necessária! :)

Pode ser esse link (pela equipe PEX) também fornece algumas idéias.

Vou começar sndBigger - É uma função muito simples, mas você pode escrever algumas propriedades que devem manter sobre isso. Por exemplo, o que acontece quando você reverte os valores na 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

EDITAR: Esta regra prop_sndBiggerSwap Nem sempre segura (veja o comentário por KVB). No entanto, o seguinte deve estar correto:

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

Sobre a pairs função, KVB Já postou algumas boas idéias. Além disso, você pode verificar se transformar a lista transformada de volta em uma lista de elementos retorna a lista original (você precisará lidar com o caso quando a lista de entrada for ímpar - dependendo do que o pairs função deve fazer neste caso):

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

Por splitOn, podemos testar coisas semelhantes - se você concatenar todas as listas retornadas, ela deve dar a lista original (isso não verifica o comportamento de divisão, mas é bom começar - pelo menos garante que nenhum elemento seja perdido).

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

Não tenho certeza se Fscheck Pode lidar com isso (!) Porém, porque a propriedade tem uma função como um argumento (por isso precisaria gerar "funções aleatórias"). Se isso não funcionar, você precisará fornecer algumas propriedades mais específicas com alguma função manuscrita f. Em seguida, implementando o cheque que f retorna true para todos os pares adjacentes nas listas divididas (como KVB sugere) não é realmente tão difícil:

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

Provavelmente a única última coisa que você poderia verificar é que f retorna false Quando você der o último elemento de uma lista e o primeiro elemento da próxima lista. O seguinte não está totalmente completo, mas mostra o caminho a seguir:

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

A última amostra também mostra que você deve verificar se o splitOn A função pode retornar uma lista vazia como parte da lista retornada de resultados (porque nesse caso, você não conseguiu encontrar o primeiro/último elemento).

Para algum código (por exemplo, sndBigger), a implementação é tão simples que qualquer propriedade será pelo menos tão complexa quanto o código original; portanto, testar via fscheck pode não fazer sentido. No entanto, para as outras duas funções, aqui estão algumas coisas que você pode verificar:

  • pairs
    • O que se espera quando o comprimento original não é divisível por dois? Você pode verificar se esse é o comportamento correto.
    • List.map fst (pairs x) = evenEntries x e List.map snd (pairs x) = oddEntries x Para funções simples evenEntries e oddEntries que você pode escrever.
  • splitOn
    • Se eu entender sua descrição de como a função deve funcionar, você pode verificar condições como "para cada lista no resultado de splitOn f l, não há duas entradas consecutivas satisfazem F "e" Tomando listas (l1,l2) a partir de splitOn f l emparelhados, f (last l1) (first l2) Segura ". Infelizmente, a lógica aqui provavelmente será comparável em complexidade à própria implementação.
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top