F#可以使用哪些单元测试框架

时间:2011-04-14 17:49:17

标签: unit-testing f# fsunit f#-unquote

我正在寻找能够利用该语言的独特功能的框架。我知道FsUnit。你会推荐别的吗?为什么?

3 个答案:

答案 0 :(得分:26)

我自己的单元测试库Unquote利用F# quotations允许您将测试断言编写为普通的,静态检查的F#布尔表达式,并自动生成很好的逐步测试失败消息。例如,以下失败的xUnit测试

[<Fact>]
let ``demo Unquote xUnit support`` () =
    test <@ ([3; 2; 1; 0] |> List.map ((+) 1)) = [1 + 3..1 + 0] @>

生成以下失败消息

Test 'Module.demo Unquote xUnit support' failed: 

([3; 2; 1; 0] |> List.map ((+) 1)) = [1 + 3..1 + 0]
[4; 3; 2; 1] = [4..1]
[4; 3; 2; 1] = []
false

        C:\File.fs(28,0): at Module.demo Unquote xUnit support()

FsUnit和Unquote有类似的任务:允许您以惯用的方式编写测试,并生成信息性的失败消息。但是FsUnit实际上只是NUnit Constraints的一个小包装器,创建了一个隐藏可组合函数调用背后的对象构造的DSL。但它需要付出代价:在断言中丢失静态类型检查。例如,以下内容在FsUnit

中有效
[<Test>]
let test1 () =
    1 |> should not (equal "2")

但是使用Unquote,你可以获得F#的所有静态类型检查功能,因此等效的断言甚至无法编译,从而阻止我们在测试代码中引入错误

[<Test>] //yes, Unquote supports both xUnit and NUnit automatically
let test2 () =
    test <@ 1 <> "2" @> //simple assertions may be written more concisely, e.g. 1 <>! "2"
    //           ^^^
    //Error 22 This expression was expected to have type int but here has type string

此外,由于引用能够在编译时捕获有关断言表达式的更多信息,因此失败消息也更丰富。例如,失败的FsUnit断言1 |> should not (equal 1)产生消息

Test 'Test.Swensen.Unquote.VerifyNunitSupport.test1' failed: 
  Expected: not 1
  But was:  1
    C:\Users\Stephen\Documents\Visual Studio 2010\Projects\Unquote\VerifyNunitSupport\FsUnit.fs(11,0): at FsUnit.should[a,a](FSharpFunc`2 f, a x, Object y)
    C:\Users\Stephen\Documents\Visual Studio 2010\Projects\Unquote\VerifyNunitSupport\VerifyNunitSupport.fs(29,0): at Test.Swensen.Unquote.VerifyNunitSupport.test1()

失败的Unquote断言1 <>! 1会产生以下失败消息(请注意更清晰的堆栈跟踪)

Test 'Test.Swensen.Unquote.VerifyNunitSupport.test1' failed: 

1 <> 1
false

    C:\Users\Stephen\Documents\Visual Studio 2010\Projects\Unquote\VerifyNunitSupport\VerifyNunitSupport.fs(29,0): at Test.Swensen.Unquote.VerifyNunitSupport.test1()

当然,从我在本答案开头的第一个例子中,您可以看到Unquote表达式和失败消息的丰富性和复杂性。

使用普通F#表达式作为FsUnit DSL的测试断言的另一个主要好处是它非常适合开发单元测试的F#过程。我认为很多F#开发人员都是在FSI的帮助下开发和测试代码的。因此,从ad-hoc FSI测试到正式测试非常容易。实际上,除了对xUnit和NUnit的特殊支持之外(尽管也支持任何基于异常的单元测试框架),所有Unquote运算符也在FSI会话中工作。

答案 1 :(得分:3)

我还没有尝试过Unquote,但我觉得我必须提到FsCheck: http://fscheck.codeplex.com/ 这是Haskells QuickCheck库的一个端口,您不需要指定要执行的特定测试,而是指定有关函数的哪些属性应该成立。 对我来说,这比使用传统测试要困难一些,但是一旦你弄清楚这些属性,你就会有更多可靠的测试。请阅读简介:http://fscheck.codeplex.com/wikipage?title=QuickStart&referringTitle=Home

我猜混合使用FsCheck和Unquote是理想的选择。

答案 2 :(得分:1)

您可以尝试Expecto;它具有您可能喜欢的一些功能:

    整个
  • F#语法,作为值进行测试;编写普通的F#生成测试
  • 使用内置的Expect模块或外部库(如Unquote)进行断言
  • 默认情况下进行并行测试
  • 测试您的Hopac代码或异步代码; Expecto始终不同步
  • 通过liary 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