F # desarrollo y pruebas unitarias?

107

Acabo de comenzar con F #, que es mi primer lenguaje funcional. He estado trabajando casi exclusivamente con C # y disfruto mucho de cómo F # me lleva a repensar cómo escribo el código. Un aspecto que encuentro un poco desorientador es el cambio en el proceso de escritura de código. He estado usando TDD durante años en C # y realmente aprecio tener pruebas unitarias para saber dónde estoy.

Hasta ahora, mi proceso con F # ha sido escribir algunas funciones, jugar con ellas con la consola interactiva hasta que esté "razonablemente" seguro de que funcionan, y ajustar y combinar. Esto funciona bien en problemas a pequeña escala como el Proyecto Euler, pero no puedo imaginarme construir algo grande de esa manera.

¿Cómo abordan las personas las pruebas unitarias y la creación de un conjunto de pruebas para un programa de F #? ¿Existe un equivalente a TDD? Se agradece cualquier sugerencia o pensamiento.

Mathias
fuente
1
expert-fsharp.com/CodeSamples/Forms/… muestra un ejemplo simple del uso de NUnit con F #.
itowlson
relacionado: stackoverflow.com/questions/5667372/… (Unquote es mucho más que una nota al pie / comentario, ya que está dentro del conjunto de respuestas en esta página)
Ruben Bartelink
Una cosa que falta en estas respuestas es un ejemplo adecuado de Foq, AutoFixture.AutoFoq y AutoFixture.xUnit aliados a la inferencia de tipo de F #. Vea trelford.com/blog/post/test5.aspx y trelford.com/blog/post/fstestlang.aspx para una muestra y algún día escribiré una respuesta adecuada aquí
Ruben Bartelink

Respuestas:

77

Los desarrolladores impulsados ​​por pruebas deben sentirse como en casa en lenguajes funcionales como F #: las pequeñas funciones que dan resultados determinísticamente repetibles se prestan perfectamente a las pruebas unitarias. También hay capacidades en el lenguaje F # que facilitan las pruebas de escritura. Tomemos, por ejemplo, Expresiones de objetos . Puede escribir muy fácilmente falsificaciones para funciones que toman como entrada un tipo de interfaz.

En todo caso, F # es un lenguaje orientado a objetos de primera clase y puede usar las mismas herramientas y trucos que usa cuando hace TDD en C #. También hay algunas herramientas de prueba escritas en o específicamente para F #:

Matthew Podwysocki escribió una gran serie sobre pruebas unitarias en lenguajes funcionales. Tío Bob también escribió un pensamiento que provoca el artículo aquí .

Ray Vernagus
fuente
9
También he desarrollado (y estoy desarrollando activamente) una biblioteca de pruebas unitarias específica de F # llamada Unquote : code.google.com/p/unquote . Le permite escribir aserciones de prueba como expresiones booleanas F # simples, verificadas estáticamente usando F # Quotations y produce automáticamente buenos mensajes de error de prueba. Funciona sin configuración con soporte especial para xUnit.net y NUnit y generalmente admite cualquier marco de prueba de unidades basado en excepciones. Incluso funciona dentro de las sesiones de FSI, lo que permite una migración perfecta de pruebas interactivas a conjuntos de pruebas formales.
Stephen Swensen
También está Pex , aunque es un poco más difícil de asimilar.
Benjol
1
El tío Bob Link parece muerto
Aage
22

Yo uso NUnit, y no me parece tan difícil de leer ni tan oneroso de escribir:

open NUnit.Framework

[<TestFixture>]
type myFixture() = class

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

end

Dado que mi código es una mezcla de F # y otros lenguajes .Net, me gusta el hecho de que escribo las pruebas unitarias básicamente de la misma manera y con una sintaxis similar tanto en F # como en C #.

David Glaubman
fuente
4
Después de leer otras respuestas aquí, probé FSUnit y creo que es genial. Funciona bien con TestDriven.Net (al igual que NUnit), fomenta un estilo fluido de escritura de pruebas autodocumentadas y, como afirma Ray, "se siente más cómodo en los idiomas F #". ¡Nada mal para 21 líneas de código! (Y un par de recomendaciones de diseño / nomenclatura). Dos notas rápidas: 1. La DLL FSUnit precompilada no funcionó para mí. La construcción desde la fuente (FsUnit.NUnit-0.9.0.fs) solucionó el problema. 2. TestDriven.Net no reconoce los nombres de TextFixture que lucen `like this`. Se reconocen los nombres de las pruebas que utilizan la forma de doble marca.
David Glaubman
15

Eche un vistazo a FsCheck , una herramienta de prueba automática para F #, es básicamente un puerto de QuickCheck de Haskell. Le permite proporcionar una especificación del programa, en forma de propiedades que las funciones o métodos deben satisfacer, y FsCheck prueba que las propiedades cumplen en un gran número de casos generados aleatoriamente.

Página CodePlex de FsCheck

Página de autor de FsCheck

primodemus
fuente
Sí, creo que FsCheck ofrece mucho más que los marcos tradicionales de prueba unitaria como NUnit, etc.
Robert
11

Como sugiere dglaubman, puede usar NUnit. xUnit.net también proporciona soporte para esto y funciona bien con TestDriven.net . El código se ve similar a las pruebas NUnit pero sin el requisito de envolver la prueba en un tipo contenedor.

#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
fuente
A partir de 1.9.1, las nuevas sobrecargas de Xunit parecen estar causando estragos en mi F #.
Rick Minerich
@RickMinerich He experimentado lo mismo que le sucede a mi código. Acabo de terminar agregando anotaciones de tipo explícitas para que se elija la sobrecarga correcta. Sin embargo, esto no añadir más ruido a su código por desgracia.
Erik Schierboom
11

Creo que esta es una pregunta muy interesante sobre la que yo mismo me he preguntado mucho. Mis pensamientos hasta ahora son solo pensamientos, así que tómalos por lo que son.

Creo que la red de seguridad de un conjunto de pruebas automatizadas es un activo demasiado valioso para dejarlo ir, por muy atractiva que pueda ser esa consola interactiva, así que planeo continuar escribiendo pruebas unitarias como siempre lo he hecho.

Una de las principales fortalezas de .NET son las capacidades de varios idiomas. Sé que pronto escribiré código de producción de F #, pero mi plan es escribir pruebas unitarias en C # para facilitar mi camino hacia lo que es para mí un nuevo lenguaje. De esta manera, también puedo probar que lo que escribo en F # será compatible con C # (y otros lenguajes .NET).

Con este enfoque, entiendo que hay ciertas características de F # que solo puedo usar internamente en mi código F #, pero no exponerlas como parte de mi API pública, pero lo aceptaré, tal como acepto hoy que hay ciertas cosas C # me permite expresar (me gusta uint) que no cumplen con CLS, por lo que me abstengo de usarlos.

Mark Seemann
fuente
2
¿Cómo iba tu plan? ¿Fue fácil probar el código f # con el código c #? Empecé a aprender f # y mi plan es escribir una parte de mi proyecto en f # y tengo la misma idea: escribir pruebas unitarias en c # para f # también.
Peter Porfy
@Marcar alguna actualización? También estoy teniendo dificultades para entrar en flujo usando TDD con F #.
Scott Nimrod
1
@ScottNimrod Varias actualizaciones: cuatro de mis cursos de Pluralsight son sobre pruebas o TDD con F #. También encontrará muchas grabaciones gratuitas de conferencias en mi perfil de Lanyrd . Finalmente, está mi blog .
Mark Seemann
3
@ScottNimrod No puedo recomendar tomarse el tiempo y / o pagar el dinero para ver el conjunto completo de cursos de PS de Mark lo suficiente - lo encajará todo en su cabeza con máxima eficiencia. Si bien puede o no aplicarse a sus necesidades específicas, la "Arquitectura funcional en F #" también conecta muchos puntos y también debe considerarse seriamente.
Ruben Bartelink
7

Podrías echar un vistazo a FSUnit , aunque todavía no lo he usado, puede que valga la pena intentarlo. Ciertamente mejor que usar, por ejemplo, NUnit (nativo) en F #.

ShdNx
fuente
1
ShdNx, ¿por qué recomendarías contra NUnit? El libro F # de Don Syme muestra NUnit para probar y se parece bastante al uso de NUnit en C #. FSUnit DSL se ve bien, pero para las personas que ya están familiarizadas con NUnit (Mathias ha "estado usando TDD durante años"), ¿es su experiencia que usar NUnit con F # es más problemático que con C # o VB?
itowlson
Secundo el comentario de itowlson y la pregunta. Definitivamente NUnit en F # parece bastante extraño, pero además de eso, ¿conoce problemas específicos que hacen que sea una buena idea usar otra cosa?
Mathias
1
Yo diría que "verse bastante extraño" suele ser una razón convincente para encontrar algo mejor. Parecer extraño significa difícil de leer y difícil de leer significa errores. (Supongo que "lucir extraño" es completamente diferente a "lucir nuevo y / o desconocido": lo desconocido se volverá familiar, lo extraño seguirá siendo extraño.)
James Moore
1
Para ser honesto (como mencioné en mi respuesta), todavía no he usado FSUnit, pero he leído que es muy doloroso usar NUnit en F #. Lo siento si no es así.
ShdNx
4
Para que quede claro, todavía tendrá TestFixtures y Test miembros si usa FsUnit. Lo que no tendrá son las llamadas estándar de Assert.X. FsUnit simplemente le brinda una envoltura sobre esta parte de NUnit que lo hace más cómodo en el lenguaje F #.
Ray Vernagus
1

A pesar de llegar un poco tarde a la fiesta, me gustaría darle la bienvenida a Mathias a F # (más vale tarde que nunca;)) y decirle que quizás te guste mi biblioteca de pruebas unitarias, Expecto

Expecto tiene algunas características que pueden gustarle:

  • Sintaxis de F # en todas partes, pruebas como valores; escribe F # simple para generar pruebas
  • Utilice el módulo Expect incorporado o una biblioteca externa como Unquote para las afirmaciones
  • Pruebas paralelas por defecto
  • Pruebe su código Hopac o su código Async; Expecto es asincrónico en todo
  • Registro y métricas conectables a través de Logary Facade; escriba fácilmente adaptadores para sistemas de compilación, o utilice el mecanismo de tiempo para crear un panel de InfluxDB + Grafana de los tiempos de ejecución de sus pruebas
  • Soporte integrado para BenchmarkDotNet
  • Incorporar soporte para FsCheck; facilita la construcción de pruebas con datos generados / aleatorios o la construcción de modelos invariantes del espacio de estado de su objeto / actor

-

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
fuente