Daniels のコメントに同意します。外部関数が正しく機能する場合は、内部関数をテストする必要はありません。内部関数は、実際には関連するべきではない実装の詳細です (特に、出力が入力以外に依存しない機能的なコードでは)。for
C# では、メソッド内のループまたは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)