4

在 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}

在不改变代码的嵌套性质的情况下,我如何测试lowerupper正常工作?

4

1 回答 1

7

我同意 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)
于 2012-05-31T21:06:40.123 回答