F # 开发和单元测试?

我刚开始学习 F # ,这是我的第一个函数式语言。我一直在准专业地使用 C # ,很喜欢 F # 如何引导我重新思考如何编写代码。我发现一个有点令人迷惑的方面是编写代码过程中的变化。我已经在 C # 中使用 TDD 很多年了,非常感谢有单元测试来了解我现在的情况。

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

人们如何进行单元测试和构建 F # 程序的测试套件?是否存在相当于 TDD 的东西?任何建议或想法都是值得赞赏的。

19263 次浏览

You could have a look at FSUnit - though I haven't used it yet, it might worth a try. Certainly better than using for example (native) NUnit in F#.

I think this is a very interesting question that I have wondered about a lot myself. My thoughts so far are only thoughts, so take them for what they are.

I think that the safety net of an automated test suite is too valuable an asset to let go, however alluring that interactive console may be, so I plan to continue writing unit tests as I've always done.

One of the main strengths of .NET is the cross-language capabilities. I know I'm going to be writing F# production code soon, but my plan is to write unit tests in C# to ease my way into what is for me a new language. In this way, I also get to test that what I write in F# will be compatible with C# (and other .NET languages).

With this approach, I understand that there are certain features of F# that I can only use internally in my F# code, but not expose as part of my public API, but I will accept that, just as I accept today that there are certain things C# allows me to express (like uint) that aren't CLS compliant, and so I refrain from using them.

Test-driven developers should feel right at home in functional languages like F#: small functions that give deterministically repeatable results lend themselves perfectly to unit tests. There are also capabilities in the F# language that facilitate writing tests. Take, for example, Object Expressions. You can very easily write fakes for functions that take as their input an interface type.

If anything, F# is a first-class object-oriented language and you can use the same tools and tricks that you use when doing TDD in C#. There are also some testing tools written in or specifically for F#:

Matthew Podwysocki wrote a great series on unit testing in functional languages. Uncle Bob also wrote a thought provoking article here.

Have a look at FsCheck, an automatic testing tool for F#, is basically a port of Haskell's QuickCheck. It allows you to provide a specification of the program, in the form of properties that the functions or methods should satisfy, and FsCheck tests that the properties hold in a large number of randomly generated cases.

FsCheck CodePlex Page

FsCheck Author Page

I use NUnit, and it doesn't strike me as hard to read or onerous to write:

open NUnit.Framework


[<TestFixture>]
type myFixture() = class


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


end

Since my code is a mix of F# and other .Net languages, I like the fact that I write the unit tests in basically the same fashion and with similar syntax in both F# and C#.

As dglaubman suggests you can use NUnit. xUnit.net also provides support for this and works well with TestDriven.net. The code looks similar to NUnit tests but without the requirement to wrap the test in a containing type.

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

Despite being a bit late to the party, I'd like to welcome Mathias to F# (better late than never ;)) and chime in that you might like my unit testing library, Expecto

Expecto has some features you might like:

  • F# syntax throughout, tests as values; write plain F# to generate tests
  • Use the built-in Expect module, or an external lib like Unquote for assertions
  • Parallel tests by default
  • Test your Hopac code or your Async code; Expecto is async throughout
  • Pluggable logging and metrics via Logary Facade; easily write adapters for build systems, or use the timing mechanism for building an InfluxDB+Grafana dashboard of your tests' execution times
  • Built in support for BenchmarkDotNet
  • Build in support for FsCheck; makes it easy to build tests with generated/random data or building invariant-models of your object's/actor's state space

--

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/