How to capture output with XUnit 2.0 and FSharp style tests
Asked Answered
A

2

18

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.

https://github.com/xunit/xunit/blob/e64f566b75f93cd3cec27f950759d82832bfe44b/src/xunit.execution/Sdk/Frameworks/Runners/TestClassRunner.cs#L90

I'm pretty sure this is an overlooked case. There is no injection of the helper into static classes.

Alveolus answered 12/8, 2015 at 14:22 Comment(1)
Could this work for you instead? https://mcmap.net/q/326113/-grabbing-the-output-sent-to-console-out-from-within-a-unit-test You don't write about the motivation for wanting to capture the output, so it may not apply, but I thought I wanted to point to that option, just in case.Yongyoni
F
21

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.

Fosdick answered 12/8, 2015 at 14:57 Comment(3)
@TomasPetricek , as always, you saved my day :) Thanks a lot!Levis
It appears this doesn't work with FsCheck's [<Property>]Invercargill
I still do not get any output in VS Code.Parturient
H
2

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


Handicraftsman answered 3/3, 2022 at 12:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.