mat*_*ely 4 testing f# unit-testing functional-testing
在F#中,我想对具有多级嵌套函数的函数执行单元测试.我希望能够单独测试嵌套函数,但我不知道如何调用它们.在调试时,这些嵌套函数中的每一个都被调用为一种函数对象,但我不知道是否可以在编译时访问它们.
我不想改变我正在使用的嵌套方案,因为它在功能上最有意义地使它们以这种方式嵌套,因为在每个嵌套级别存在一些事实上的"继承"某些函数参数.
这样的事情可能吗?如果没有,单元测试嵌套函数的一般过程是什么?他们是否使用额外的参数进行单独测试,然后插入其嵌套位置后再也无法再进行测试?
非常小的例子:
let range a b =
let lower = ceil a |> int
let upper = floor b |> int
if lower > upper then
Seq.empty
else
seq{ for i in lower..upper -> i}
Run Code Online (Sandbox Code Playgroud)
如何在不改变代码的嵌套特性的情况下测试lower
或upper
正常工作?
我同意Daniels的评论 - 如果外部函数正常工作,你不应该测试任何内部函数.内部函数实际上是一个不应该相关的实现细节(特别是在功能代码中,输出不依赖于输入以外的任何东西).在C#中,您也不会测试方法中的for
循环或while
循环是否正常工作.
如果内部函数和外部函数都太复杂,那么最好将内部函数作为单独的函数编写.
也就是说,当然,您可以使用反射来混淆编译的程序集并调用内部函数.内部函数被编译为具有构造函数的类,该构造函数接受闭包(捕获外部函数的值)和Invoke
获取实际参数的方法.
以下简单示例有效,但我没有在更现实的测试中进行测试:
open NUnit.Framework
// Function with 'inner' that captures the argument 'a' and takes additional 'x'
let outer a b =
let inner x = x + a + 1
(inner a) * (inner b)
// Unit tests that use reflection in a hacky way to test 'inner'
[<TestFixture>]
module Tests =
open System
open System.Reflection
// Runs the specified compiled function - assumes that 'name' of inner functions
// is unique in the current assembly (!) and that you can correctly guess what
// are the variables captured by the closure (!)
let run name closure args =
// Lots of unchecked assumptions all the way through...
let typ =
Assembly.GetExecutingAssembly().GetTypes()
|> Seq.find (fun typ ->
let at = typ.Name.IndexOf('@')
(at > 0) && (typ.Name.Substring(0, at) = name) )
let flags = BindingFlags.Instance ||| BindingFlags.NonPublic
let ctor = typ.GetConstructors(flags) |> Seq.head
let f = ctor.Invoke(closure)
let invoke = f.GetType().GetMethod("Invoke")
invoke.Invoke(f, args)
/// Test that 'inner 10' returns '14' if inside outer where 'a = 3'
[<Test>]
let test () =
Assert.AreEqual(run "inner" [| box 3 |] [| box 10 |], 14)
Run Code Online (Sandbox Code Playgroud)