Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Stash of a spike to grab TickSpec Scenarios during the xUnit 2 discovery phase in order to surface the Scenarios as individual Tests in the VS Test Runner list. AFAICT this will never work as it will require all of TickSpec's object model and more (the compiled code) to be serializable
namespace TickSpec
open System
open System.Reflection
open System.Threading.Tasks
open Xunit
open Xunit.Abstractions
open Xunit.Sdk
module Impl =
let scenariosfromtestMethod (testMethod: Xunit.Abstractions.ITestMethod) : seq<Scenario> =
Console.WriteLine ("scenariosfromtestMethod" + testMethod.Method.Name)
testMethod.Method.ToRuntimeMethod().Invoke(null, null) :?> seq<Scenario>
// Stolen from
type private TestCase(bus, methd, display, label) =
// NOTE: we're passing the test label as "test method arguments". We have to do this,
// because otherwise all our tests cases will look identical to Xunit, because they
// come from the same test method. But if we pass the label as "arguments", Xunit will
// include it in the test case identity, thus making these tests distinct.
inherit XunitTheoryTestCase(bus, TestMethodDisplay.ClassAndMethod, methd)
/// Required for Xunit's runner to be able to traverse AppDomains etc.
new() = new TestCase(null, null, null, null)
interface ITestCase with
member __.DisplayName = display
override this.RunAsync(_, bus:IMessageBus, _,_, _) : Task<RunSummary> =
let post (m: #IMessageSinkMessage) = bus.QueueMessage m |> ignore
let test = XunitTest(this, this.DisplayName)
let runFeature = async {
let runScenario (scenario : Scenario) =
let reportSuccess () =
post <| TestPassed(test, 0m, "")
RunSummary(Total = 1)
let reportExn (ex : exn) =
let ex = if ex.InnerException = null then ex else ex.InnerException
post <| TestFailed(test, 0m, "", ex)
RunSummary(Total = 1, Failed = 1)
Console.WriteLine scenario.Name
reportSuccess ()
with ex -> reportExn ex
post (TestStarting test)
let summary = RunSummary()
scenariosfromtestMethod methd
|> Seq.find (fun x-> x.Name = label)
|> runScenario
|> summary.Aggregate
return summary }
Async.StartAsTask(runFeature)//, ?cancellationToken = cancel.Token)
let discover (testMethod: Xunit.Abstractions.ITestMethod) bus : IXunitTestCase seq =
let makeCase (scenario : Scenario) =
let label = scenario.Name
let display = sprintf "%s.%s %s" testMethod.TestClass.Class.Name testMethod.Method.Name label
new TestCase(bus, testMethod, display, label)
scenariosfromtestMethod testMethod
|> makeCase
|> Seq.cast
/// Xunit Marker Atrribute; Used to tag entrypoints from which Scenarios are yielded which can then be Discovered (via FeatureDiscover) and run
[<AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)>]
[<XunitTestCaseDiscoverer("TickSpec.FeatureDiscoverer", "FSharpXunit2")>]
type FeatureAttribute() =
inherit FactAttribute()
/// Xunit Discoverer; Used by an Xunit runner to Discover tests based on the FeatureAttribute tagging each Feature entry-point
type FeatureDiscoverer(bus) =
interface IXunitTestCaseDiscoverer with
member __.Discover(_, testMethod, _) : IXunitTestCase seq = testMethod bus
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.