Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Jan 27, 2026

Fix XUnit TestOutputHelper Lifetime Issue ✅

  • Analyze the issue - XUnit TestOutputHelper lifetime problem
  • Understand root cause - TestOutputHelper captured in closures outlives test lifetime
  • Create Helpers.fs with centralized safeWriteLine function
  • Update PropertyAttribute.fs to use Helpers.safeWriteLine
  • Update CheckExtensions.fs to use Helpers.safeWriteLine
  • Update Runner.fs to use Helpers.safeWriteLine
  • Move tests to F# in FsCheck.Test project
  • Fix Directory.Packages.props - Remove implicit ReferenceAssemblies package
  • Build FsCheck.Xunit successfully
  • Verify all tests pass (11 XUnit tests including 5 new lifetime tests)
  • Complete

Solution Implemented:
Created a centralized Helpers module with a single safeWriteLine function that wraps TestOutputHelper.WriteLine() calls in try-catch blocks to handle InvalidOperationException when the test has completed and the helper is no longer active. This prevents the error "There is no currently active test" when closures that capture the TestOutputHelper are called after the test lifetime ends.

Root Cause:
The TestOutputHelper was being captured in closures for the Every and EveryShrink callbacks when verbose mode is enabled. These closures can outlive the test's lifetime, especially when tests run in parallel or sequentially. When XUnit 2.9.0+ introduced stricter lifetime management for TestOutputHelper, these lingering references would throw InvalidOperationException when accessed.

Test Results:

  • Created 5 comprehensive F# tests in PropertyAttributeTests.fs:
    • 2 Property tests with parameters to generate multiple test cases
    • 1 Verbose Property test that specifically exercises Every and EveryShrink callbacks
    • 2 Fact tests for mixed test scenarios
  • All 11 XUnit tests pass consistently
  • No regressions detected

Files Changed:

  • src/FsCheck.Xunit/Helpers.fs - New internal module with centralized safeWriteLine helper
  • src/FsCheck.Xunit/FsCheck.Xunit.fsproj - Added Helpers.fs to compilation order
  • src/FsCheck.Xunit/PropertyAttribute.fs - Updated to use Helpers.safeWriteLine
  • src/FsCheck.Xunit/CheckExtensions.fs - Updated to use Helpers.safeWriteLine
  • src/FsCheck.Xunit/Runner.fs - Updated to use Helpers.safeWriteLine
  • tests/FsCheck.Test/Fscheck.XUnit/PropertyAttributeTests.fs - Added F# lifetime tests
  • Directory.Packages.props - Removed implicit Microsoft.NETFramework.ReferenceAssemblies package reference
Original prompt

This section details on the original issue you should resolve

<issue_title>Lifetime problem with Xunit: InvalidOperationException: There is no currently active test.</issue_title>
<issue_description>I get reproducable runtime error on one of two tests if I run all tests of the following test class / project. If I run just one test (e.g. manually in IDE Rider) everything looks good (no error).

Just guessing: maybe related to #657

Exception

System.InvalidOperationException: There is no currently active test.

System.InvalidOperationException
There is no currently active test.
   at Xunit.Sdk.TestOutputHelper.GuardInitialized() in /_/src/xunit.execution/Sdk/Frameworks/TestOutputHelper.cs:line 52
   at Xunit.Sdk.TestOutputHelper.QueueTestOutput(String output) in /_/src/xunit.execution/Sdk/Frameworks/TestOutputHelper.cs:line 61
   at Xunit.Sdk.TestOutputHelper.WriteLine(String message) in /_/src/xunit.execution/Sdk/Frameworks/TestOutputHelper.cs:line 109
   at TestProject1.UnitTest1.Test2() in TestProject1\UnitTest1.cs:line 18
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
   at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

Here is my C# project:

UnitTest1.cs

public class UnitTest1(Xunit.Abstractions.ITestOutputHelper testOutputHelper)
{
    [FsCheck.Xunit.Property]
    public void Test1()
    {
        testOutputHelper.WriteLine($"{nameof(Test1)}");
    }

    [Xunit.Fact]
    public void Test2()
    {
        testOutputHelper.WriteLine($"{nameof(Test2)}");
    }
}

TestProject1.csproj

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        <IsTestProject>true</IsTestProject>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="FsCheck.Xunit" Version="3.0.0" />
        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
        <PackageReference Include="xunit" Version="2.9.3" />
        <PackageReference Include="xunit.runner.visualstudio" Version="3.0.1" />
    </ItemGroup>
</Project>

Reproducable by just running dotnet test in project folder.

No error if I downgrade to xunit 2.8.1 / FsCheck.Xunit 2.16.6.

<Project Sdk="Microsoft.NET.Sdk">
    <PropertyGroup>
        <TargetFramework>net8.0</TargetFramework>
        <IsTestProject>true</IsTestProject>
    </PropertyGroup>
    <ItemGroup>
        <PackageReference Include="FsCheck.Xunit" Version="2.16.6" />
        <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
        <PackageReference Include="xunit" Version="2.8.1" />
        <PackageReference Include="xunit.runner.visualstudio" Version="3.0.1" />
    </ItemGroup>
</Project>
```</issue_description>

## Comments on the Issue (you are @copilot in this section)

<comments>
<comment_new><author>@kurtschelfthout</author><body>
I can't reproduce this...</body></comment_new>
<comment_new><author>@kurtschelfthout</author><body>
The testoutputhelper is suspicious. xunit has a bit of rough edges around handling it - see the comments in the code in the xUnit Property attribute in FsCheck.Xunit. Perhaps it's related to that.</body></comment_new>
</comments>


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 3 commits January 27, 2026 22:21
Co-authored-by: kurtschelfthout <164917+kurtschelfthout@users.noreply.github.com>
Co-authored-by: kurtschelfthout <164917+kurtschelfthout@users.noreply.github.com>
Co-authored-by: kurtschelfthout <164917+kurtschelfthout@users.noreply.github.com>
Copilot AI changed the title [WIP] Fix lifetime problem with Xunit causing InvalidOperationException Guard TestOutputHelper.WriteLine calls against lifetime exceptions Jan 27, 2026
Copilot AI requested a review from kurtschelfthout January 27, 2026 22:31
Copy link
Member

@kurtschelfthout kurtschelfthout left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Add a new file/internal module called Helpers, add safeWriteLine there once, and call that function from everywhere.
  • move the tests to FsCheck.Test and write them in F#

- Created Helpers.fs with centralized safeWriteLine function
- Updated PropertyAttribute.fs, CheckExtensions.fs, and Runner.fs to use the helper
- Moved TestOutputHelperLifetimeTests from C# to F# in PropertyAttributeTests.fs
- Reverted unnecessary C# test project changes

Co-authored-by: kurtschelfthout <164917+kurtschelfthout@users.noreply.github.com>
Copy link
Contributor Author

Copilot AI commented Jan 29, 2026

  • Add a new file/internal module called Helpers, add safeWriteLine there once, and call that function from everywhere.
  • move the tests to FsCheck.Test and write them in F#

Done in commit 81dad6e:

  • Created Helpers.fs with a single internal safeWriteLine function
  • Updated PropertyAttribute.fs, CheckExtensions.fs, and Runner.fs to call the centralized helper
  • Moved tests from C# to F# in PropertyAttributeTests.fs with 5 test cases (2 Property, 1 Verbose Property, 2 Fact)
  • All 11 XUnit tests pass

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Lifetime problem with Xunit: InvalidOperationException: There is no currently active test.

2 participants