Desenvolvimento F # e teste de unidade?

107

Acabei de começar com F #, que é minha primeira linguagem funcional. Tenho trabalhado quase exclusivamente com C # e gosto muito de como o F # me leva a repensar como escrevo código. Um aspecto que considero um pouco desorientador é a mudança no processo de escrever código. Tenho usado o TDD há anos em C # e realmente aprecio ter testes de unidade para saber onde estou.

Até agora, meu processo com o F # tem sido escrever algumas funções, brincar com elas com o console interativo até estar "razoavelmente" certo de que funcionam e ajustar e combinar. Isso funciona bem em problemas de pequena escala como o Projeto Euler, mas não consigo imaginar construir algo grande dessa forma.

Como as pessoas abordam o teste de unidade e a construção de um conjunto de testes para um programa F #? Existe um equivalente ao TDD? Quaisquer sugestões ou pensamentos são apreciados.

Mathias
fonte
1
expert-fsharp.com/CodeSamples/Forms/… mostra um exemplo simples de uso de NUnit com F #.
itowlson 01 de
consulte stackoverflow.com/questions/1468772/…
Mauricio Scheffer
related: stackoverflow.com/questions/5667372/… (Unquote é muito mais do que uma nota de rodapé / comentário, pois está dentro do conjunto de respostas nesta página)
Ruben Bartelink
Uma coisa que falta nessas respostas é um exemplo adequado de Foq, AutoFixture.AutoFoq e AutoFixture.xUnit aliados à inferência de tipo do F #. Veja trelford.com/blog/post/test5.aspx e trelford.com/blog/post/fstestlang.aspx para um degustador e algum dia escreverei uma resposta adequada aqui
Ruben Bartelink

Respostas:

77

Os desenvolvedores orientados a testes devem se sentir em casa em linguagens funcionais como F #: pequenas funções que fornecem resultados deterministicamente repetíveis se prestam perfeitamente a testes de unidade. Também existem recursos na linguagem F # que facilitam a escrita de testes. Considere, por exemplo, Expressões de Objeto . Você pode facilmente escrever falsificações para funções que tomam como entrada um tipo de interface.

De qualquer forma, F # é uma linguagem orientada a objetos de primeira classe e você pode usar as mesmas ferramentas e truques que usa ao fazer TDD em C #. Existem também algumas ferramentas de teste escritas em ou especificamente para F #:

Matthew Podwysocki escreveu uma ótima série sobre testes de unidade em linguagens funcionais. Tio Bob também escreveu um artigo instigante aqui .

Ray Vernagus
fonte
9
Também desenvolvi (e estou desenvolvendo ativamente) uma biblioteca de teste de unidade específica do F # chamada Unquote: code.google.com/p/unquote . Ele permite que você escreva asserções de teste como expressões booleanas F # verificadas estaticamente usando F # Quotations e produz automaticamente boas mensagens de falha de teste. Ele funciona sem configuração com suporte especial para xUnit.net e NUnit e geralmente oferece suporte a qualquer estrutura de teste de unidade baseada em exceção. Ele funciona até mesmo em sessões FSI, permitindo a migração perfeita de testes interativos para conjuntos de testes formais.
Stephen Swensen,
Pex também, embora seja um pouco mais difícil de grocar.
Benjol,
1
Ligação tio bob parece morto
Aage
22

Eu uso o NUnit e não me parece tão difícil de ler ou oneroso de escrever:

open NUnit.Framework

[<TestFixture>]
type myFixture() = class

    [<Test>]
    member self.myTest() =
       //test code

end

Como meu código é uma mistura de F # e outras linguagens .Net, gosto do fato de escrever os testes de unidade basicamente da mesma maneira e com sintaxe semelhante em F # e C #.

David Glaubman
fonte
4
Depois de ler outras respostas aqui, experimentei o FSUnit e acho que é ótimo. Ele funciona bem com TestDriven.Net (assim como o NUnit), incentiva um estilo fluido de escrever testes de autodocumentação e, como afirma Ray, "se sente mais à vontade nas linguagens F #". Nada mal para 21 linhas de código! (E algumas recomendações de layout / nomenclatura). Duas notas rápidas: 1. O FSUnit DLL pré-compilado não funcionou para mim. Compilar a partir da fonte (FsUnit.NUnit-0.9.0.fs) corrigiu o problema. 2. TestDriven.Net não reconhece nomes TextFixture que parecem `like this`. Os nomes dos testes usando a forma de marcação dupla são reconhecidos.
David Glaubman
15

Dê uma olhada no FsCheck , uma ferramenta de teste automática para F #, é basicamente uma versão do QuickCheck de Haskell. Ele permite que você forneça uma especificação do programa, na forma de propriedades que as funções ou métodos devem satisfazer, e o FsCheck testa que as propriedades mantêm em um grande número de casos gerados aleatoriamente.

Página FsCheck CodePlex

Página do autor FsCheck

primodemo
fonte
Sim, acho que o FsCheck oferece muito mais do que estruturas de teste de unidade tradicionais, como NUnit etc.
Robert
11

Como o dglaubman sugere, você pode usar o NUnit. xUnit.net também fornece suporte para isso e funciona bem com TestDriven.net . O código é semelhante aos testes NUnit, mas sem a necessidade de envolver o teste em um tipo de contenção.

#light

// Supply a module name here not a combination of module and namespace, otherwise
// F# cannot resolve individual tests nfrom the UI.
module NBody.DomainModel.FSharp.Tests

open System
open Xunit

open Internal

[<Fact>]
let CreateOctantBoundaryReordersMinMax() =
    let Max = VectorFloat(1.0, 1.0, 1.0)
    let Min = VectorFloat(-1.0, -1.0, -1.0)

    let result = OctantBoundary.create Min Max

    Assert.Equal(Min, result.Min)     
    Assert.Equal(Max, result.Max) 
Ade Miller
fonte
A partir de 1.9.1, as novas sobrecargas do Xunit parecem estar causando estragos no meu F #.
Rick Minerich
@RickMinerich Eu experimentei a mesma coisa acontecendo com meu código. Acabei adicionando anotações de tipo explícito para que a sobrecarga correta fosse escolhida. No entanto, isso não adicionar mais ruído ao seu código infelizmente.
Erik Schierboom
11

Acho que esta é uma questão muito interessante, sobre a qual eu mesmo me questiono muito. Meus pensamentos até agora são apenas pensamentos, então aceite-os como são.

Acho que a rede de segurança de um conjunto de testes automatizados é um ativo valioso demais para ser deixado de lado, por mais atraente que o console interativo possa ser, então pretendo continuar escrevendo testes de unidade como sempre fiz.

Um dos principais pontos fortes do .NET são os recursos de várias linguagens. Eu sei que estarei escrevendo código de produção em F # em breve, mas meu plano é escrever testes de unidade em C # para facilitar meu caminho para o que para mim é uma nova linguagem. Dessa forma, também posso testar se o que escrevo em F # será compatível com C # (e outras linguagens .NET).

Com essa abordagem, entendo que existem certos recursos do F # que só posso usar internamente em meu código F #, mas não expor como parte da minha API pública, mas vou aceitar isso, assim como aceito hoje que há certas coisas C # me permite expressar (como uint) que não são compatíveis com CLS e, portanto, evito usá-los.

Mark Seemann
fonte
2
Como está indo seu plano? Foi fácil testar o código f # com código c #? Comecei a aprender f # e meu plano é escrever alguma parte do meu projeto em f # e tenho a mesma ideia: escrever testes de unidade em c # para f # também.
Peter Porfy de
@Marcar alguma atualização? Também estou tendo dificuldade em entrar no fluxo usando TDD com F #.
Scott Nimrod
1
@ScottNimrod Algumas atualizações: quatro dos meus cursos Pluralsight são sobre testes ou TDD com F #. Você também encontrará muitas gravações gratuitas de palestras em conferências no meu perfil do Lanyrd . Finalmente, há meu blog .
Mark Seemann
3
@ScottNimrod Não posso recomendar que você gaste um tempo e / ou pague o suficiente para ver o conjunto completo de cursos PS do Mark - isso vai encaixar tudo na sua cabeça com a máxima eficiência. Embora possa ou não se aplicar às suas necessidades específicas, a "Arquitetura funcional em F #" também conecta muitos pontos e também deve ser fortemente considerada.
Ruben Bartelink
7

Você poderia dar uma olhada no FSUnit - embora eu não o tenha usado ainda, vale a pena tentar. Certamente melhor do que usar, por exemplo, NUnit (nativo) em F #.

ShdNx
fonte
1
ShdNx, por que você recomendaria contra o NUnit? O livro F # de Don Syme mostra o NUnit para teste e é muito semelhante ao uso do NUnit em C #. O FSUnit DSL parece legal, mas para pessoas já familiarizadas com o NUnit (Mathias "vem usando TDD há anos"), é sua experiência que usar o NUnit com F # é mais problemático do que com C # ou VB?
itowlson 01 de
Eu segundo o comentário e a pergunta. Definitivamente, NUnit em F # parece muito estranho, mas, além disso, você está ciente de problemas específicos que tornam uma boa ideia usar outra coisa?
Mathias
1
Eu diria que "parecer muito estranho" geralmente é uma razão convincente para encontrar algo melhor. Parecer estranho significa difícil de ler e difícil de ler significa bugs. (Estou assumindo que "parecer estranho" é completamente diferente de "parecer novo e / ou desconhecido" - desconhecido se tornará familiar, estranho permanecerá estranho.)
James Moore
1
Para ser honesto (como mencionei na minha resposta), ainda não usei o FSUnit, mas li que é muito doloroso usar o NUnit no F #. Desculpe se não for assim.
ShdNx
4
Para ser claro, você ainda terá TestFixtures e membros de teste se usar FsUnit. O que você não terá são as chamadas Assert.X padrão. O FsUnit simplesmente fornece um invólucro em torno dessa parte do NUnit que o torna mais familiar na linguagem F #.
Ray Vernagus
1

Apesar de estar um pouco atrasado para a festa, gostaria de dar as boas-vindas a Mathias em F # (antes tarde do que nunca;)) e avisar que você pode gostar de Expecto

Expecto tem alguns recursos de que você pode gostar:

  • Sintaxe de F # em toda, testes como valores; escreva F # simples para gerar testes
  • Use o módulo Expect integrado ou uma biblioteca externa como Unquote para asserções
  • Testes paralelos por padrão
  • Teste seu código Hopac ou código Async; Expecto é totalmente assíncrono
  • Registro e métricas plugáveis ​​via Logary Facade; escrever facilmente adaptadores para sistemas de construção ou usar o mecanismo de tempo para construir um painel InfluxDB + Grafana dos tempos de execução de seus testes
  • Suporte integrado para BenchmarkDotNet
  • Construir em suporte para FsCheck; torna mais fácil construir testes com dados gerados / aleatórios ou construir modelos invariantes do espaço de estado do seu objeto / ator

-

open Expecto

let tests =
  test "A simple test" {
    let subject = "Hello World"
    Expect.equal subject "Hello World" "The strings should equal"
  }

[<EntryPoint>]
let main args =
  runTestsWithArgs defaultConfig args tests

https://github.com/haf/expecto/

Henrik
fonte