F#开发和单元测试?


107

我刚开始使用F#,这是我的第一门功能语言。我一直在与C#进行半排他性的合作,并且非常喜欢F#如何使我重新思考如何编写代码。我发现一个令人迷惑的方面是编写代码过程中的变化。我已经在C#中使用TDD多年了,非常感谢进行单元测试以了解我的位置。

到目前为止,我使用F#的过程是编写一些函数,并在交互式控制台中使用它们,直到我“合理”地确定它们可以工作,然后进行调整和组合。这对于诸如Euler Project之类的小规模问题非常有效,但我无法想象以这种方式构建大型项目。

人们如何进行单元测试并为F#程序构建测试套件?是否有与TDD相当的产品?任何指针或想法表示赞赏。


1
expert-fsharp.com/CodeSamples/Forms/…显示了将NUnit与F#一起使用的简单示例。
itowlson'1


相关:stackoverflow.com/questions/5667372/…(取消引号不只是脚注/评论,因为它在此页面的答案集中)
Ruben Bartelink 2013年

这些答案中缺少的一件事是与F#的类型推断相关联的Foq,AutoFixture.AutoFoq和AutoFixture.xUnit的正确示例。有关品尝者的信息,请参见trelford.com/blog/post/test5.aspxtrelford.com/blog/post/fstestlang.aspx。总有一天,我会在此处写下一个正确的答案
Ruben Bartelink,2013年

Answers:


77

测试驱动的开发人员应该使用F#之类的功能语言,感到宾至如归:提供确定性可重复结果的小型函数非常适合单元测试。F#语言中还具有一些有助于编写测试的功能。以Object Expressions为例。您可以很容易地为使用接口类型作为输入的函数编写伪造品。

如果有的话,F#是一流的面向对象语言,您可以使用在C#中进行TDD时使用的相同工具和技巧。还有一些专门针对F#编写的测试工具:

Matthew Podwysocki撰写了有关功能语言的单元测试的精彩系列文章。鲍伯叔叔也在这里写了一篇发人深省的文章。


9
我还开发了(并且正在积极开发)一个名为Unquote的F#特定单元测试库:code.google.com/p/unquote。它允许您使用F#报价将测试断言编写为普通的,静态检查的F#布尔表达式,并自动生成良好的测试失败消息。它无需配置即可工作,并且特别支持xUnit.net和NUnit,并且通常支持任何基于异常的单元测试框架。它甚至可以在FSI会议中使用,从而允许从交互式测试到正式测试套件的无缝迁移。
Stephen Swensen

也有Pex,尽管很难理解。
Benjol

1
鲍伯叔叔的链接似乎已经死了
Aage

22

我使用NUnit,它并不使我难以阅读或难以编写:

open NUnit.Framework

[<TestFixture>]
type myFixture() = class

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

end

因为我的代码是F#和其他.Net语言的混合,所以我喜欢这样的事实,即我以基本相同的方式编写单元测试,并且在F#和C#中使用相似的语法。


4
在阅读了本文的其他回复之后,我尝试了FSUnit,我认为它很棒。它与TestDriven.Net(与NUnit一样)可以很好地协同工作,鼓励编写自文档测试的流畅风格,并且正如Ray所言,“在F#语言中更常见”。对于21行代码来说还不错!(以及一些布局/命名建议)。有两个快速注意事项:1.预编译的FSUnit DLL对我不起作用。从源代码构建(FsUnit.NUnit-0.9.0.fs)解决了该问题。2. TestDriven.Net无法识别看起来像的TextFixture名称`like this`。可以识别使用两次勾号形式的测试名称。
David Glaubman 2010年

15

看看FsCheck,它是F#的自动测试工具,基本上是Haskell的QuickCheck的移植端口。它允许您以函数或方法应满足的属性的形式提供程序规范,并通过FsCheck测试该属性在大量随机生成的情况下所具有的性能。

FsCheck CodePlex页面

FsCheck作者页面


是的,我认为FsCheck提供了更多比传统的单元测试框架,如NUnit的等
罗伯特·

11

正如dglaubman建议的那样,您可以使用NUnit。xUnit.net也对此提供了支持,并与TestDriven.net一起很好地工作。该代码看起来类似于NUnit测试,但是不需要将测试包装在包含类型中。

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

从1.9.1开始,Xunit的新重载似乎对我的F#造成了严重破坏。
里克·梅里奇

@RickMinerich我的代码也经历了同样的事情。我最后只是添加了显式类型注释,以便选择正确的重载。但是,不幸的是,这确实给代码增加了更多的噪音。
Erik Schierboom

11

我认为这是一个非常有趣的问题,我本人对此很纳闷。到目前为止,我的想法只是想法,因此请以其为准。

我认为,自动化测试套件的安全网实在是太宝贵了,无法放手,尽管它可能会吸引交互式控制台,所以我打算像往常一样继续编写单元测试。

.NET的主要优势之一是跨语言功能。我知道我即将写F#生产代码,但是我的计划是用C#编写单元测试,以简化我对新语言的理解。这样,我还可以测试我用F#编写的内容是否与C#(和其他.NET语言)兼容。

通过这种方法,我了解到F#的某些功能只能在F#代码中内部使用,而不能作为公共API的一部分公开,但是我将接受,就像我今天接受的那样, C#允许我表达(例如uint)不符合CLS的语言,因此我避免使用它们。


2
您的计划进展如何?用C#代码测试f#代码很容易吗?我开始学习f#,我的计划是用f#编写项目的一部分,我的想法是:也可以在c#中为f#编写单元测试。
彼得·波菲

@标记任何更新?我也很难在F#中使用TDD。
Scott Nimrod

1
@ScottNimrod进行了一些更新:我的Pluralsight课程中有四是关于使用F#进行测试或TDD的。您还可以在Lanyrd的个人资料中找到许多会议演讲的免费录音。最后是我的博客
Mark Seemann

3
@ScottNimrod我不建议您花时间和/或花钱足够好地观看Mark的完整PS课程集-它会以最高的效率将它们全都融合在一起。尽管“ F#中的功能体系结构”可能会或可能不会满足您的特定需求,但它也连接了许多点,因此也应予以认真考虑。
Ruben Bartelink

7

您可以看一下FSUnit-尽管我还没有使用它,但值得一试。当然比在F#中使用例如(本机)NUnit更好。


1
ShdNx,您为什么建议反对NUnit?Don Syme的F#书展示了NUnit进行测试,它看起来与在C#中使用NUnit非常相似。FSUnit DSL看起来很酷,但是对于已经熟悉NUnit的人们(Mathias已经“使用TDD多年”),您的经验是,将NUnit与F#一起使用比在C#或VB上存在更多问题吗?
itowlson'1

我第二次给itowlson评论,并提出问题。绝对可以肯定,F#中的NUnit看起来很奇怪,但是除此之外,您是否意识到特定的问题,因此最好使用其他方法?
Mathias

1
我会说“看起来很奇怪”通常是找到更好的东西的诱人原因。看起来奇怪意味着难以阅读,而难以阅读则意味着错误。(我假设“看起来很奇怪”与“看起来很新和/或不熟悉”完全不同-陌生将变得熟悉,奇数将保持奇怪。)
James Moore 2010年

1
老实说(正如我在响应中提到的那样),我还没有使用过FSUnit,但是我读到在F#中使用NUnit非常痛苦。抱歉,不是这样。
ShdNx

4
需要说明的是,如果使用FsUnit,您将仍然拥有TestFixtures和Test成员。您将没有标准的Assert.X调用。FsUnit只是为NUnit的这一部分提供了一个包装器,使它在F#语言中更加自如。
雷·韦尔纳格斯

1

尽管参加聚会有点晚,但我还是想欢迎Mathias加入F#(迟到总比没有好;)。因为您可能喜欢我的单元测试库Expecto

Expecto具有一些您可能想要的功能:

  • 全文采用F#语法,将其作为值进行测试;编写普通的F#生成测试
  • 使用内置的Expect模块或外部库(例如Unquote)进行断言
  • 默认情况下并行测试
  • 测试您的Hopac代码或异步代码;Expecto在整个过程中都是异步的
  • 通过Logary Facade可插拔的日志记录和指标;轻松编写用于构建系统的适配器,或使用计时机制来构建测试执行时间的InfluxDB + Grafana仪表板
  • 内置对BenchmarkDotNet的支持
  • 建立对FsCheck的支持;使用生成的/随机的数据或建立对象/行为者状态空间的不变模型来构建测试变得容易

-

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/

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.