mirror of
https://github.com/Smaug123/unofficial-nunit-runner
synced 2025-10-06 01:48:38 +00:00
Cope with test fixtures which have non-static members (#22)
This commit is contained in:
@@ -15,6 +15,7 @@
|
|||||||
</None>
|
</None>
|
||||||
<Compile Include="TestAppDomain.fs" />
|
<Compile Include="TestAppDomain.fs" />
|
||||||
<Compile Include="TestCaseData.fs" />
|
<Compile Include="TestCaseData.fs" />
|
||||||
|
<Compile Include="TestNonStatic.fs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
31
Consumer/TestNonStatic.fs
Normal file
31
Consumer/TestNonStatic.fs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
namespace Consumer
|
||||||
|
|
||||||
|
open System.Threading
|
||||||
|
open FsUnitTyped
|
||||||
|
open NUnit.Framework
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module NonStaticTestHelpers =
|
||||||
|
let count = ref 0
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
type TestNonStatic () =
|
||||||
|
let count = ref 0
|
||||||
|
|
||||||
|
member this.Thing = "i'm a thing"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
member this.Foo () =
|
||||||
|
Interlocked.Increment NonStaticTestHelpers.count |> ignore<int>
|
||||||
|
Interlocked.Increment count |> ignore<int>
|
||||||
|
this.Thing |> shouldEqual "i'm a thing"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
static member AnotherTest () =
|
||||||
|
Interlocked.Increment NonStaticTestHelpers.count |> ignore<int>
|
||||||
|
1 |> shouldEqual 1
|
||||||
|
|
||||||
|
[<OneTimeTearDown>]
|
||||||
|
member _.TearDown () =
|
||||||
|
count.Value |> shouldEqual 1
|
||||||
|
NonStaticTestHelpers.count.Value |> shouldEqual 2
|
@@ -26,6 +26,7 @@ module TestFixture =
|
|||||||
(setUp : MethodInfo list)
|
(setUp : MethodInfo list)
|
||||||
(tearDown : MethodInfo list)
|
(tearDown : MethodInfo list)
|
||||||
(test : MethodInfo)
|
(test : MethodInfo)
|
||||||
|
(containingObject : obj)
|
||||||
(args : obj[])
|
(args : obj[])
|
||||||
: Result<unit, TestFailure list>
|
: Result<unit, TestFailure list>
|
||||||
=
|
=
|
||||||
@@ -40,7 +41,7 @@ module TestFixture =
|
|||||||
| head :: rest ->
|
| head :: rest ->
|
||||||
let result =
|
let result =
|
||||||
try
|
try
|
||||||
head.Invoke (null, args) |> Ok
|
head.Invoke (containingObject, args) |> Ok
|
||||||
with e ->
|
with e ->
|
||||||
Error (UserMethodFailure.Threw (head.Name, e))
|
Error (UserMethodFailure.Threw (head.Name, e))
|
||||||
|
|
||||||
@@ -103,6 +104,7 @@ module TestFixture =
|
|||||||
let private runTestsFromMember
|
let private runTestsFromMember
|
||||||
(setUp : MethodInfo list)
|
(setUp : MethodInfo list)
|
||||||
(tearDown : MethodInfo list)
|
(tearDown : MethodInfo list)
|
||||||
|
(containingObject : obj)
|
||||||
(test : SingleTestMethod)
|
(test : SingleTestMethod)
|
||||||
: Result<TestMemberSuccess, TestMemberFailure> list
|
: Result<TestMemberSuccess, TestMemberFailure> list
|
||||||
=
|
=
|
||||||
@@ -143,7 +145,10 @@ module TestFixture =
|
|||||||
match test.Kind, values with
|
match test.Kind, values with
|
||||||
| TestKind.Data data, None ->
|
| TestKind.Data data, None ->
|
||||||
data
|
data
|
||||||
|> Seq.map (fun args -> runOne setUp tearDown test.Method (Array.ofList args) |> normaliseError)
|
|> Seq.map (fun args ->
|
||||||
|
runOne setUp tearDown test.Method containingObject (Array.ofList args)
|
||||||
|
|> normaliseError
|
||||||
|
)
|
||||||
| TestKind.Data _, Some _ ->
|
| TestKind.Data _, Some _ ->
|
||||||
[
|
[
|
||||||
"Test has both the TestCase and Values attributes. Specify one or the other."
|
"Test has both the TestCase and Values attributes. Specify one or the other."
|
||||||
@@ -151,7 +156,10 @@ module TestFixture =
|
|||||||
|> TestMemberFailure.Malformed
|
|> TestMemberFailure.Malformed
|
||||||
|> Error
|
|> Error
|
||||||
|> Seq.singleton
|
|> Seq.singleton
|
||||||
| TestKind.Single, None -> runOne setUp tearDown test.Method [||] |> normaliseError |> Seq.singleton
|
| TestKind.Single, None ->
|
||||||
|
runOne setUp tearDown test.Method containingObject [||]
|
||||||
|
|> normaliseError
|
||||||
|
|> Seq.singleton
|
||||||
| TestKind.Single, Some vals ->
|
| TestKind.Single, Some vals ->
|
||||||
let combinatorial =
|
let combinatorial =
|
||||||
Option.defaultValue Combinatorial.Combinatorial test.Combinatorial
|
Option.defaultValue Combinatorial.Combinatorial test.Combinatorial
|
||||||
@@ -163,7 +171,8 @@ module TestFixture =
|
|||||||
|> Seq.toList
|
|> Seq.toList
|
||||||
|> List.combinations
|
|> List.combinations
|
||||||
|> Seq.map (fun args ->
|
|> Seq.map (fun args ->
|
||||||
runOne setUp tearDown test.Method (Array.ofList args) |> normaliseError
|
runOne setUp tearDown test.Method containingObject (Array.ofList args)
|
||||||
|
|> normaliseError
|
||||||
)
|
)
|
||||||
| Combinatorial.Sequential ->
|
| Combinatorial.Sequential ->
|
||||||
let maxLength = vals |> Seq.map (fun i -> i.Count) |> Seq.max
|
let maxLength = vals |> Seq.map (fun i -> i.Count) |> Seq.max
|
||||||
@@ -174,7 +183,7 @@ module TestFixture =
|
|||||||
vals
|
vals
|
||||||
|> Array.map (fun param -> if i >= param.Count then null else param.[i].Value)
|
|> Array.map (fun param -> if i >= param.Count then null else param.[i].Value)
|
||||||
|
|
||||||
yield runOne setUp tearDown test.Method args |> normaliseError
|
yield runOne setUp tearDown test.Method containingObject args |> normaliseError
|
||||||
}
|
}
|
||||||
| TestKind.Source _, Some _ ->
|
| TestKind.Source _, Some _ ->
|
||||||
[
|
[
|
||||||
@@ -199,11 +208,12 @@ module TestFixture =
|
|||||||
for arg in args.GetValue null :?> System.Collections.IEnumerable do
|
for arg in args.GetValue null :?> System.Collections.IEnumerable do
|
||||||
yield
|
yield
|
||||||
match arg with
|
match arg with
|
||||||
| :? Tuple<obj, obj> as (a, b) -> runOne setUp tearDown test.Method [| a ; b |]
|
| :? Tuple<obj, obj> as (a, b) ->
|
||||||
|
runOne setUp tearDown test.Method containingObject [| a ; b |]
|
||||||
| :? Tuple<obj, obj, obj> as (a, b, c) ->
|
| :? Tuple<obj, obj, obj> as (a, b, c) ->
|
||||||
runOne setUp tearDown test.Method [| a ; b ; c |]
|
runOne setUp tearDown test.Method containingObject [| a ; b ; c |]
|
||||||
| :? Tuple<obj, obj, obj, obj> as (a, b, c, d) ->
|
| :? Tuple<obj, obj, obj, obj> as (a, b, c, d) ->
|
||||||
runOne setUp tearDown test.Method [| a ; b ; c ; d |]
|
runOne setUp tearDown test.Method containingObject [| a ; b ; c ; d |]
|
||||||
| arg ->
|
| arg ->
|
||||||
let argTy = arg.GetType ()
|
let argTy = arg.GetType ()
|
||||||
|
|
||||||
@@ -219,9 +229,14 @@ module TestFixture =
|
|||||||
if isNull argsMem then
|
if isNull argsMem then
|
||||||
failwith "Unexpectedly could not call `.Arguments` on TestCaseData"
|
failwith "Unexpectedly could not call `.Arguments` on TestCaseData"
|
||||||
|
|
||||||
runOne setUp tearDown test.Method (argsMem.Invoke (arg, [||]) |> unbox<obj[]>)
|
runOne
|
||||||
|
setUp
|
||||||
|
tearDown
|
||||||
|
test.Method
|
||||||
|
containingObject
|
||||||
|
(argsMem.Invoke (arg, [||]) |> unbox<obj[]>)
|
||||||
else
|
else
|
||||||
runOne setUp tearDown test.Method [| arg |]
|
runOne setUp tearDown test.Method containingObject [| arg |]
|
||||||
|> normaliseError
|
|> normaliseError
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -253,9 +268,32 @@ module TestFixture =
|
|||||||
let run (filter : TestFixture -> SingleTestMethod -> bool) (tests : TestFixture) : int =
|
let run (filter : TestFixture -> SingleTestMethod -> bool) (tests : TestFixture) : int =
|
||||||
eprintfn $"Running test fixture: %s{tests.Name} (%i{tests.Tests.Length} tests to run)"
|
eprintfn $"Running test fixture: %s{tests.Name} (%i{tests.Tests.Length} tests to run)"
|
||||||
|
|
||||||
|
let containingObject =
|
||||||
|
let methods =
|
||||||
|
seq {
|
||||||
|
match tests.OneTimeSetUp with
|
||||||
|
| None -> ()
|
||||||
|
| Some t -> yield t
|
||||||
|
|
||||||
|
match tests.OneTimeTearDown with
|
||||||
|
| None -> ()
|
||||||
|
| Some t -> yield t
|
||||||
|
|
||||||
|
yield! tests.Tests |> Seq.map (fun t -> t.Method)
|
||||||
|
}
|
||||||
|
|
||||||
|
methods
|
||||||
|
|> Seq.tryPick (fun mi ->
|
||||||
|
if not mi.IsStatic then
|
||||||
|
Some (Activator.CreateInstance mi.DeclaringType)
|
||||||
|
else
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|> Option.toObj
|
||||||
|
|
||||||
match tests.OneTimeSetUp with
|
match tests.OneTimeSetUp with
|
||||||
| Some su ->
|
| Some su ->
|
||||||
match su.Invoke (null, [||]) with
|
match su.Invoke (containingObject, [||]) with
|
||||||
| :? unit -> ()
|
| :? unit -> ()
|
||||||
| ret -> failwith $"One-time setup procedure '%s{su.Name}' returned non-null %O{ret}"
|
| ret -> failwith $"One-time setup procedure '%s{su.Name}' returned non-null %O{ret}"
|
||||||
| _ -> ()
|
| _ -> ()
|
||||||
@@ -269,7 +307,7 @@ module TestFixture =
|
|||||||
eprintfn $"Running test: %s{test.Name}"
|
eprintfn $"Running test: %s{test.Name}"
|
||||||
let testSuccess = ref 0
|
let testSuccess = ref 0
|
||||||
|
|
||||||
let results = runTestsFromMember tests.SetUp tests.TearDown test
|
let results = runTestsFromMember tests.SetUp tests.TearDown containingObject test
|
||||||
|
|
||||||
for result in results do
|
for result in results do
|
||||||
match result with
|
match result with
|
||||||
@@ -287,7 +325,7 @@ module TestFixture =
|
|||||||
| Some td ->
|
| Some td ->
|
||||||
try
|
try
|
||||||
// TODO: all these failwiths hide errors that we caught and wrapped up nicely above
|
// TODO: all these failwiths hide errors that we caught and wrapped up nicely above
|
||||||
if not (isNull (td.Invoke (null, [||]))) then
|
if not (isNull (td.Invoke (containingObject, [||]))) then
|
||||||
failwith $"TearDown procedure '%s{td.Name}' returned non-null"
|
failwith $"TearDown procedure '%s{td.Name}' returned non-null"
|
||||||
with :? TargetInvocationException as e ->
|
with :? TargetInvocationException as e ->
|
||||||
failwith $"One-time teardown of %s{td.Name} failed: %O{e.InnerException}"
|
failwith $"One-time teardown of %s{td.Name} failed: %O{e.InnerException}"
|
||||||
|
Reference in New Issue
Block a user