Reputation: 32192
Normally I write my unit tests in F# as
open Swensen.Unquote
open Xunit
module MyTests =
[<Fact>]
let ``SomeFunction should return 10`` () =
let a = SomeFunction()
test <@ a = 10 @>
[<Fact>]
let ``SomeOtherFunction should return 11`` () =
let a = SomeFunction()
test <@ a = 11 @>
If I wish to log to the console from xunit ( according to http://xunit.github.io/docs/capturing-output.html ) one needs to write a constructor that takes an ITestOutputHelper and then use that instead of Console.WriteLine and family.
using Xunit;
using Xunit.Abstractions;
public class MyTestClass
{
private readonly ITestOutputHelper output;
public MyTestClass(ITestOutputHelper output)
{
this.output = output;
}
[Fact]
public void MyTest()
{
var temp = "my class!";
output.WriteLine("This is output from {0}", temp);
}
}
however fsharp modules are static classes and the tests are static methods. There is no constructor to inject the output helper.
Is there a way to get access to the current output helper for the current test. I know I could rewrite my fsharp tests to be non static classes but that is undesired.
After looking at the XUnit source.
I'm pretty sure this is an overlooked case. There is no injection of the helper into static classes.
Upvotes: 18
Views: 2067
Reputation: 1792
Nowadays this solution seems to be working with let bindings as well:
module Tests
open System
open System.Text.Json
open System.Text.Json.Serialization
open Xunit
open Xunit.Abstractions
open Core.DomainModel.OnboardingModel
type SerializationTests(output: ITestOutputHelper) =
let options = JsonSerializerOptions()
do
JsonFSharpConverter(
unionEncoding = JsonUnionEncoding.InternalTag,
unionTagName = "type",
allowOverride = true
)
|> options.Converters.Add
[<Fact>]
let ``CanSerializeDiscriminatedUnion using Fsharp.SystemTextJson`` () =
let onboardingStep = InformName "test"
let serialization = JsonSerializer.Serialize(onboardingStep, options)
output.WriteLine serialization
The results will appear on the console when this command is executed:
dotnet test --logger:"console;verbosity=detailed"
Results:
[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v2.4.3+1b45f5407b (64-bit .NET 6.0.1)
[xUnit.net 00:00:00.44] Discovering: Core.DomainModel.Tests
[xUnit.net 00:00:00.47] Discovered: Core.DomainModel.Tests
[xUnit.net 00:00:00.47] Starting: Core.DomainModel.Tests
[xUnit.net 00:00:00.58] Finished: Core.DomainModel.Tests
Passed Tests+SerializationTests.CanSerializeDiscriminatedUnion using Fsharp.SystemTextJson [58 ms]
Standard Output Messages:
["InformName","test"]
Test Run Successful.
Total tests: 1
Passed: 1
Total time: 1.1842 Seconds
Upvotes: 2
Reputation: 243051
If xUnit does not have any alternative mechanism for injection of the parameters, then I guess the only option is to define the tests as methods in an F# object type. I also prefer writing tests as functions using let
, but the following simple object type does not look too bad:
open Swensen.Unquote
open Xunit
open Xunit.Abstractions
type MyTests(output:ITestOutputHelper) =
[<Fact>]
member __.``SomeFunction should return 10`` () =
let a = SomeFunction()
output.WriteLine("Some function returned {0}", a)
test <@ a = 10 @>
It would be nice if xUnit supported some other option for this - I suspect that they might be open to suggestions, if it is something that would not be too awkward (perhaps using a method parameter?)
But unless xUnit adds support for some other method, I think you'll need to use F# object with methods.
Upvotes: 21