-
-
Notifications
You must be signed in to change notification settings - Fork 108
Enhance NUnit analyzer converters with generic type constraints, Has.Member, and Apartment attribute support #4260
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
…ttribute in NUnit migration Co-authored-by: thomhurst <[email protected]>
…tion to base class Co-authored-by: thomhurst <[email protected]>
|
@copilot More scenarios to consider: I also found a problem with converting // NUnit source
public class WithinDeltaFailureExample
{
[Test]
public void Example()
{
Assert.That(10, Is.EqualTo(5).Within(2));
}
}It gets converted to // TUnit actual
public class WithinDeltaFailureExample
{
[Test]
public async Task Example()
{
await Assert.That(10).IsEqualTo(Is.EqualTo(5).Within(2));
}
}When the correct conversion is // TUnit expected
public class WithinDeltaFailureExample
{
[Test]
public async Task Example()
{
await Assert.That(10).IsEqualTo(5).Within(2);
}
}Another case: // NUnit source
Assert.DoesNotThrow(() => x += y);Should become // TUnit expected
await Assert.That(() => x += y).ThrowsNothing();// NUnit source
Assert.Ignore();Should become // TUnit expected
Skip.Test("Reason");// NUnit source
Assert.Fail();Should become // TUnit expected
Fail.Test("Reason"); |
…t.Fail conversions Co-authored-by: thomhurst <[email protected]>
Added support for all the scenarios you mentioned in commit ca35aae:
|
Co-authored-by: thomhurst <[email protected]>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR enhances the NUnit migration analyzer code fixers with support for additional NUnit patterns that were missing from the conversion tooling. The changes enable automatic conversion of generic type constraints, collection membership assertions, apartment threading attributes, chained constraint modifiers, and special assertion methods like DoesNotThrow, Ignore, and Fail.
Key Changes
- Added support for generic type constraint assertions (
Is.TypeOf<T>(),Is.InstanceOf<T>()) with proper mapping to TUnit'sIsTypeOf<T>()andIsAssignableTo<T>() - Implemented conversion for
Has.Member(item)to.Contains(item) - Added
[Apartment(ApartmentState.STA)]to[STAThreadExecutor]attribute conversion - Implemented chained constraint support (e.g.,
Is.EqualTo(5).Within(2)) - Added conversion for
Assert.DoesNotThrow(),Assert.Ignore(), andAssert.Fail() - Refactored
CreateTUnitGenericAssertionmethod to baseAssertionRewriterclass for reusability
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
TUnit.Analyzers/Migrators/Base/MigrationHelpers.cs |
Added mapping for "Apartment" → "STAThreadExecutor" attribute name conversion |
TUnit.Analyzers.CodeFixers/NUnitMigrationCodeFixProvider.cs |
Implemented conversion logic for generic constraints, Has.Member, chained constraints, DoesNotThrow, and Apartment attributes; updated Fail/Skip assertion conversions |
TUnit.Analyzers.CodeFixers/Base/AssertionRewriter.cs |
Moved CreateTUnitGenericAssertion method from derived class to base class; changed IsEmptyOrNullMessage visibility to protected for reuse |
TUnit.Analyzers.Tests/NUnitMigrationAnalyzerTests.cs |
Added 11 comprehensive test cases covering all new conversion patterns |
Summary
This PR enhances the NUnit migration analyzer code fixers with support for additional NUnit patterns.
Completed Work
Generic Type Constraint Support
Is.TypeOf<T>()→IsTypeOf<T>()Is.InstanceOf<T>()→IsAssignableTo<T>()Is.Not.TypeOf<T>()→IsNotTypeOf<T>()Is.Not.InstanceOf<T>()→IsNotAssignableTo<T>()Collection Membership Support
Has.Member(item)→.Contains(item)Apartment Attribute Support
[Apartment(ApartmentState.STA)]→[STAThreadExecutor]Chained Constraint Support
Is.EqualTo(5).Within(2)→IsEqualTo(5).Within(2)DoesNotThrow Support
Assert.DoesNotThrow(() => action)→await Assert.That(() => action).ThrowsNothing()Assert.Ignore Support
Assert.Ignore("reason")→Skip.Test("reason")Assert.Fail Support
Assert.Fail("reason")→Fail.Test("reason")Code Quality
CreateTUnitGenericAssertionto baseAssertionRewriterclassIsEmptyOrNullMessageprotected to eliminate duplicationTests
Original prompt
This section details on the original issue you should resolve
<issue_title>[Feature]: Enhance NUnit analyzer converters</issue_title>
<issue_description>### Problem Statement
While converting ReactiveUI project to use TUnit (from NUnit), I discovered some missing cases in the converter. I ended up writing a bunch of these and would be happy to create a PR for you. So it's a large project with a established NUnit base so be useful for your conversion process.
Below is a list of what I found:
Problem: TUnit requires explicit
[Test]attribute when using[Arguments]or[MethodDataSource], but NUnit allows[TestCase]/[TestCaseSource]alone.Current Behavior: Build fails with cryptic error
Expected Behavior: Analyzer should detect and offer code fix
Proposed Fix:
TUnit1001- "Method with data attribute must have [Test] attribute"[Test]attributeProblem: NUnit's
Assert.Ignore()has no direct equivalent; requires throwingSkipTestExceptionCurrent Behavior: No guidance, developers must discover pattern through documentation
Impact: Analyzers that rely on
ISymbolInfoto detect NUnit types may failProblem: NUnit's lifecycle attributes need explicit scope in TUnit
Proposed Fix:
TUnit3001- "Use [Before(HookType.X)] instead of [SetUp]"Problem: NUnit's
[Apartment(ApartmentState.STA)]maps to[STAThreadExecutor]Proposed Fix:
TUnit3002- "Use [STAThreadExecutor] instead of [Apartment(ApartmentState.STA)]"// NUnit - AssemblyInfo.cs
[assembly: Parallelizable(ParallelScope.All)]
[assembly: LevelOfParallelism(4)]
// TUnit - no assembly-level equivalent
// Conservative migration: [assembly: NotInParallel]
// Better: Per-class [NotInParallel] where needed
Recommendation: Consider adding extension methods for migration compatibility:
Problem: NUnit's scope-based multiple assertions differ from TUnit's lambda approach
Proposed Fix:
TUnit5001- "Convert...💡 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.