An approach
You can resort to using reflection to invoke your private test methods: you will have one public NUnit test method that will iterate over all private methods in the assembly that call those that have the Test attribute. The big side of this approach is that you can only see one failed test method at a time (but maybe you could learn something creative, like using parameterized tests to fix this).
Example
Program.fsi
namespace MyNs module Program = val visibleMethod: int -> int
Program.fs
namespace MyNs open NUnit.Framework module Program = let implMethod1 xy = x + y [<Test>] let testImpleMethod1 () = Assert.AreEqual(implMethod1 1 1, 2) let implMethod2 xyz = x + y + z [<Test>] let testImpleMethod2 () = Assert.AreEqual(implMethod2 1 1 1, 3) let implMethod3 xyzr = x + y + z + r [<Test>] let testImpleMethod3 () = Assert.AreEqual(implMethod3 1 1 1 1, -1) let implMethod4 xyzrs = x + y + z + r + s [<Test>] let testImpleMethod4 () = Assert.AreEqual(implMethod4 1 1 1 1 1, 5) let visibleMethod x = implMethod1 xx + implMethod2 xxx + implMethod3 xxxx
TestProxy.fs (implementation of our "approach")
module TestProxy open NUnit.Framework [<Test>] let run () = ///we only want static (ie let bound functions of a module), ///non-public methods (exclude any public methods, including this method, ///since those will not be skipped by nunit) let bindingFlags = System.Reflection.BindingFlags.Static ||| System.Reflection.BindingFlags.NonPublic ///returns true if the given obj is of type TestAttribute, the attribute used for marking nunit test methods let isTestAttr (attr:obj) = match attr with | :? NUnit.Framework.TestAttribute -> true | _ -> false let assm = System.Reflection.Assembly.GetExecutingAssembly() let tys = assm.GetTypes() let mutable count = 0 for ty in tys do let methods = ty.GetMethods(bindingFlags) for mi in methods do let attrs = mi.GetCustomAttributes(false) if attrs |> Array.exists isTestAttr then //using stdout w/ flush instead of printf to ensure messages printed to screen in sequence stdout.Write(sprintf "running test `%s`..." mi.Name) stdout.Flush() mi.Invoke(null,null) |> ignore stdout.WriteLine("passed") count <- count + 1 stdout.WriteLine(sprintf "All %i tests passed." count)
Output Result (Using TestDriven.NET)
Note that we never get testImplMethod4 as it fails in testImpleMethod3:
running test `testImpleMethod1`...passed running test `testImpleMethod2`...passed running test `testImpleMethod3`...Test 'TestProxy.run' failed: System.Reflection.TargetInvocationException : Exception has been thrown by the target of an invocation. ----> NUnit.Framework.AssertionException : Expected: 4 But was: -1 at System.RuntimeMethodHandle._InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, SignatureStruct& sig, MethodAttributes methodAttributes, RuntimeType typeOwner) at System.RuntimeMethodHandle.InvokeMethodFast(IRuntimeMethodInfo method, Object target, Object[] arguments, Signature sig, MethodAttributes methodAttributes, RuntimeType typeOwner) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture, Boolean skipVisibilityChecks) at System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) at System.Reflection.MethodBase.Invoke(Object obj, Object[] parameters) C:\Users\Stephen\Documents\Visual Studio 2010\Projects\FsOverflow\FsOverflow\TestProxy.fs(29,0): at TestProxy.run() --AssertionException C:\Users\Stephen\Documents\Visual Studio 2010\Projects\FsOverflow\FsOverflow\Program.fs(25,0): at MyNs.Program.testImpleMethod3() 0 passed, 1 failed, 4 skipped (see 'Task List'), took 0.41 seconds (NUnit 2.5.10).