Skip to content

TUXU0001 code fixer does not convert Assert.Throws<T> exception assertions #4483

@thomhurst

Description

@thomhurst

Description

When running the TUXU0001 code fixer for xUnit migration, the code fixer does not convert Assert.Throws<T>() or Assert.ThrowsAsync<T>() exception assertions to TUnit's equivalent syntax.

Steps to Reproduce

  1. Create a test file with xUnit exception assertions:
using Xunit;

namespace MyTests
{
    public class ExceptionTests
    {
        [Fact]
        public void Throws_ArgumentException_When_Invalid()
        {
            Assert.Throws<ArgumentException>(() => DoSomething(null));
        }

        [Fact]
        public void Throws_And_Returns_Exception()
        {
            var ex = Assert.Throws<InvalidOperationException>(() => DoSomethingElse());
            Assert.Equal("Expected message", ex.Message);
        }

        [Fact]
        public async Task ThrowsAsync_InvalidOperationException()
        {
            await Assert.ThrowsAsync<InvalidOperationException>(async () => await DoSomethingAsync());
        }

        [Fact]
        public void ThrowsAny_Exception()
        {
            Assert.ThrowsAny<Exception>(() => DoSomething(null));
        }
    }
}
  1. Install TUnit package alongside xUnit
  2. Run the code fixer: dotnet format analyzers --severity warn --diagnostics TUXU0001

Expected Behavior

The exception assertions should be converted to TUnit's fluent syntax:

namespace MyTests
{
    public class ExceptionTests
    {
        [Test]
        public async Task Throws_ArgumentException_When_Invalid()
        {
            await Assert.That(() => DoSomething(null)).ThrowsException<ArgumentException>();
        }

        [Test]
        public async Task Throws_And_Returns_Exception()
        {
            var ex = await Assert.That(() => DoSomethingElse()).ThrowsException<InvalidOperationException>();
            await Assert.That(ex.Message).IsEqualTo("Expected message");
        }

        [Test]
        public async Task ThrowsAsync_InvalidOperationException()
        {
            await Assert.That(async () => await DoSomethingAsync()).ThrowsException<InvalidOperationException>();
        }

        [Test]
        public async Task ThrowsAny_Exception()
        {
            await Assert.That(() => DoSomething(null)).ThrowsException();  // or appropriate TUnit equivalent
        }
    }
}

Actual Behavior

The [Fact] attributes are converted to [Test] but the Assert.Throws<T>() calls remain unchanged:

namespace MyTests
{
    public class ExceptionTests
    {
        [Test]
        public void Throws_ArgumentException_When_Invalid()  // Not converted to async Task
        {
            Assert.Throws<ArgumentException>(() => DoSomething(null));  // Not converted!
        }
        // ...
    }
}

This causes:

  • After removing xUnit, Assert.Throws is not recognized
  • Tests using exception assertions remain synchronous (not converted to async Task)

Impact

In the Moq test suite migration, there are 398 occurrences of Assert.Throws that were not converted by the code fixer, making manual migration very labor-intensive.

Environment

  • TUnit Version: 1.11.64
  • .NET SDK: 10.0.102
  • OS: Windows

Tests to Add

Please add tests for converting:

  1. Assert.Throws<T>(() => ...)
  2. Assert.ThrowsAsync<T>(async () => ...)
  3. Assert.ThrowsAny<T>(() => ...)
  4. var ex = Assert.Throws<T>(...) (where exception is captured)
  5. Nested exception assertions
  6. Exception assertions with message validation

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions