A .NET tool that automatically converts FluentAssertions test assertions to their xUnit equivalents. Regardless of how we feel about the upcoming license changes in the next major version of FluentAssertions, I imagine many of us will find ourselves in the position of having to strip out FluentAssertions of our codebases. I know I will. This tool tries to automate that process as much as possible.
- xunit/xunit#3133
- https://www.meziantou.net/using-roslyn-to-analyze-and-rewrite-code-in-a-solution.htm
- https://stackoverflow.com/questions/31481251/applying-multiple-changes-to-a-solution-in-rosly
- ...
dotnet tool install -g FluentAssertionsMigratorcd C:\...\fluentassertions-migrator\src\
dotnet pack
dotnet tool install -g FluentAssertionsMigratorMake a backup of your solution (or use version control like a sane person) before you run this tool
Before you run this tool, be sure to restore the nuget packages of your solution. This tool uses semantic analysis in some places that will not work without these packages.
migrate-fluentassertions <path-to-solution-file>Example:
migrate-fluentassertions C:\Projects\MySolution.slnThis tool analyzes your test files and converts FluentAssertions API calls to their xUnit equivalents. It handles many common assertion patterns including:
.Should().Be()→Assert.Equal().Should().NotBe()→Assert.NotEqual().Should().BeSameAs()→Assert.Same().Should().NotBeSameAs()→Assert.NotSame().Should().BeEquivalentTo()→Assert.Equivalent()
.Should().BeTrue()→Assert.True().Should().BeFalse()→Assert.False()
.Should().BeNull()→Assert.Null().Should().NotBeNull()→Assert.NotNull()
.Should().BeEmpty()→Assert.Empty().Should().NotBeEmpty()→Assert.NotEmpty().Should().HaveCount()→Assert.Equal(count)orAssert.Single()orAssert.Empty().Should().Contain()→Assert.Contains().Should().NotContain()→Assert.DoesNotContain().Should().ContainSingle()→Assert.Single().Should().BeNullOrEmpty()→Assert.False(?.Any() ?? false).Should().NotBeNullOrEmpty()→Assert.True(?.Any()).Should().BeOneOf()→Assert.Contains().Should().ContainEquivalentOf()→Assert.Contains().Should().NotContainEquivalentOf()→Assert.DoesNotContain()
.Should().StartWith()→Assert.StartsWith().Should().EndWith()→Assert.EndsWith().Should().ContainAll()→Assert.All(strings, s => Assert.Contains(s, x))
.Should().BeOfType()→Assert.True(x is Type).Should().NotBeOfType()→Assert.False(x is Type)
.Should().Throw()→Assert.Throws().Should().NotThrow()→Assert.Nulll(Record.Exception()).Should().Throw<T>()→Assert.Throws<T>().Should().NotThrow<T>()→Assert.IsType<T>(Record.Exception()).Should().ThrowAsync()→Assert.ThrowsAsync().Should().NotThrowAsync()→Assert.Null(Record.ExceptionAsync()).Should().ThrowAsync<T>()→Assert.ThrowsAsync<T>().Should().NotThrowAsync<T>()→Assert.IsNotType<T>(Record.ExceptionAsync())
.Should().BeGreaterThan()→Assert.True(x > y).Should().BeLessThan()→Assert.True(x < y).Should().BeCloseTo()→Assert.True(x > (expected - precision) && x < (expected + precision)).Should().NotBeCloseTo()→Assert.False(x > (expected - precision) && x < (expected + precision))
.Should().BeBefore()→Assert.True(x < y).Should().NotBeBefore()→Assert.False(x < y).Should().BeAfter()→Assert.True(x > y).Should().NotBeAfter()→Assert.False(x > y).Should().BeOnOrBefore()→Assert.True(x <= y).Should().NotBeOnOrBefore()→Assert.False(x <= y).Should().BeOnOrAfter()→Assert.True(x >= y).Should().NotBeOnOrAfter()→Assert.False(x >= y)
While this tool automates a significant portion of the migration, manual review and adjustments will be needed:
- Some complex FluentAssertions may not have direct xUnit equivalents
- Custom assertion messages will need to be reformatted
- Chain assertions (using .And) will need to be split into multiple Assert statements
- Some assertions might require additional null checks or type conversions
Always review the generated code and test thoroughly after migration.
Feel free to submit issues and pull requests but I'm not making any promises. :-)
Yes, it is! If you really like this and want to give something in return, donations can be made here: https://github.com/sponsors/amoerie?frequency=one-time. Thanks!