-
-
Notifications
You must be signed in to change notification settings - Fork 108
feat: add NUnit FileAssert, DirectoryAssert, and ExpectedException support #4361
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
…pport - Add FileAssert conversion (Exists, DoesNotExist, AreEqual, AreNotEqual) - Add DirectoryAssert conversion (Exists, DoesNotExist, AreEqual, AreNotEqual) - Add ExpectedException attribute conversion to Assert.ThrowsAsync<T>() - Automatically add System.IO using when File/Directory classes are used - Support async lambdas in ExpectedException conversion when await expressions exist Co-Authored-By: Claude Opus 4.5 <[email protected]>
SummaryAdds NUnit migration support for FileAssert, DirectoryAssert, and ExpectedException attributes. Critical Issues1. DirectoryAssert.AreEqual semantic difference The conversion of Location: TUnit.Analyzers.CodeFixers/NUnitMigrationCodeFixProvider.cs:1584-1592 Impact: Tests using Recommendation:
Example of what could go wrong: // NUnit - fails if file1.txt contents differ
DirectoryAssert.AreEqual(expectedDir, actualDir);
// Migrated TUnit - only checks file names exist, not contents
await Assert.That(Directory.GetFiles(actualDir, "*", SearchOption.AllDirectories))
.IsEquivalentTo(Directory.GetFiles(expectedDir, "*", SearchOption.AllDirectories));SuggestionsNone - the rest of the implementation looks solid. The ExpectedException conversion is well-handled with proper async lambda detection, and System.IO using statements are correctly added. Previous Review StatusNo previous comments. Verdict |
… difference Address review feedback: NUnit's DirectoryAssert.AreEqual compares both file paths AND file contents recursively. The TUnit migration only compares file paths. Add a TODO comment to warn users about this limitation. Co-Authored-By: Claude Opus 4.5 <[email protected]>
SummaryAdds NUnit migration support for FileAssert, DirectoryAssert, and ExpectedException attributes. Critical IssuesNone found - The TODO comment in the generated code adequately addresses the DirectoryAssert.AreEqual limitation. Suggestions
VerdictAPPROVE - No critical issues blocking merge |
- Add FileInfo.HasSameContentAs(other) assertion for binary file comparison - Add DirectoryInfo.HasSameStructureAs(other) for path structure comparison - Add DirectoryInfo.IsEquivalentTo(other) for full directory equivalence - Add DirectoryInfo.IsNotEquivalentTo(other) negated version - Simplify NUnit FileAssert/DirectoryAssert migration to use new assertions - Update AddSystemIOUsing to detect FileInfo/DirectoryInfo object creation Co-Authored-By: Claude Opus 4.5 <[email protected]>
SummaryAdds NUnit migration support for FileAssert, DirectoryAssert, and ExpectedException attributes. Critical IssuesNone found Suggestions
Previous Review StatusPrevious review raised critical issue about DirectoryAssert.AreEqual semantic differences. This has been addressed with proper IsEquivalentTo extension that compares both structure AND file contents. Compliance with TUnit RulesAll applicable rules satisfied: No VSTest dependencies, uses modern C# features appropriately. VerdictAPPROVE - No critical issues. Suggestions are minor improvements for follow-up PRs. |
- Add FileInfo.DoesNotHaveSameContentAs(other) assertion for negated file comparison - Update NUnit migration to use DoesNotHaveSameContentAs for FileAssert.AreNotEqual - Add test for FileAssert.AreNotEqual migration Co-Authored-By: Claude Opus 4.5 <[email protected]>
SummaryAdds NUnit migration support for FileAssert, DirectoryAssert, and ExpectedException attributes with proper file content comparison. Critical IssuesNone found ✅ Suggestions1. Performance concern: File.ReadAllBytes() for large files The Location: TUnit.Assertions/Conditions/FileSystemAssertions.cs:218, 258, 407 Suggestion: Consider using streaming comparison for large files, or document the memory limitation. For example: // Stream-based comparison for large files
using (var stream1 = File.OpenRead(value.FullName))
using (var stream2 = File.OpenRead(expected.FullName))
{
byte[] buffer1 = new byte[8192];
byte[] buffer2 = new byte[8192];
// Compare chunks...
}Impact: Low - Most test files are small. This is a nice-to-have optimization for future consideration. 2. Type detection heuristics The Location: TUnit.Analyzers.CodeFixers/NUnitMigrationCodeFixProvider.cs:1513-1519, 1596-1602 Issue: This could misfire on variables like Suggestion: Use semantic model to check actual types instead of naming heuristics (mentioned in previous reviews). However, this is already a known limitation and the heuristic works for common cases. Impact: Low - Edge case that rarely occurs in practice. The migration tool is already very functional. Previous Review Status✅ All previous critical issues have been addressed:
TUnit Rules Compliance✅ Dual-Mode: N/A - No source generator or reflection mode changes Verdict✅ APPROVE - No critical issues. The suggestions are minor optimizations that can be addressed in future PRs if needed. The PR successfully implements comprehensive NUnit migration support with correct semantics. |
Path.GetRelativePath is not available in netstandard2.0, so use a Uri-based implementation as a polyfill for that target framework. Co-Authored-By: Claude Opus 4.5 <[email protected]>
SummaryAdds NUnit migration support for FileAssert, DirectoryAssert, and ExpectedException attributes with proper file content comparison. Critical Issues1. Infinite recursion in GetRelativePath method Location: TUnit.Assertions/Conditions/FileSystemAssertions.cs:515 Issue: The #else branch of the GetRelativePath method calls itself recursively without any change: #else
return GetRelativePath(relativeTo, path); // ← Infinite recursion!
#endifThis should call #else
return Path.GetRelativePath(relativeTo, path);
#endifImpact: This will cause a StackOverflowException on non-netstandard2.0 targets (.NET 6+, which is the primary target for TUnit). Suggestions1. Performance: File.ReadAllBytes() loads entire files into memory Locations:
Concern: For large files (GB-sized), this could cause memory issues. Consider streaming comparison for better memory efficiency (though most test files are small, so low priority). 2. Early optimization opportunity Location: TUnit.Assertions/Conditions/FileSystemAssertions.cs:218 The code already checks file sizes match before reading bytes in HasSameContentAs, which is good. However, in IsEquivalentTo (line 407), the size check happens after reading both files. Consider checking sizes first to short-circuit before the expensive I/O. Previous Review StatusThe author has already addressed previous critical issues in self-review: TUnit Rules Compliance✅ Dual-Mode: N/A - No changes to core engine metadata collection Verdict |
…ertions Add new public API entries for: - FileInfo.DoesNotHaveSameContentAs - FileInfo.HasSameContentAs - DirectoryInfo.HasSameStructureAs - DirectoryInfo.IsEquivalentTo - DirectoryInfo.IsNotEquivalentTo Co-Authored-By: Claude Opus 4.5 <[email protected]>
The #else branch was calling GetRelativePath recursively instead of Path.GetRelativePath, which would cause a StackOverflowException on non-netstandard2.0 targets. Co-Authored-By: Claude Opus 4.5 <[email protected]>
SummaryThis PR adds NUnit migration support for FileAssert, DirectoryAssert, and ExpectedException attributes, converting them to TUnit's equivalent assertion patterns. Critical IssuesNone found ✅ SuggestionsPerformance: Consider async file I/O for large filesThe new file comparison assertions use synchronous I/O operations ( Location: var actualBytes = File.ReadAllBytes(value.FullName);
var expectedBytes = File.ReadAllBytes(expected.FullName);Suggestion: For better performance with large files, consider using async I/O:
This is not blocking since:
But it could be a future enhancement for better scalability. Code Quality: Directory comparison allocates full file listsThe var actualFiles = value.EnumerateFiles("*", SearchOption.AllDirectories)
.Select(f => Path.GetRelativePath(value.FullName, f.FullName))
.OrderBy(p => p)
.ToList();Minor optimization opportunity: For large directory trees, this could be memory-intensive. Consider using Again, not blocking - directory comparisons in tests are typically small. Review Checklist✅ Snapshot Testing: Verdict✅ APPROVE - No critical issues This is a solid implementation of NUnit migration support. The suggestions above are minor performance optimizations that could be considered as future enhancements but don't block this PR. The code follows TUnit patterns, has excellent test coverage, and properly updates all snapshot files. |
SummaryAdds NUnit migration support for FileAssert, DirectoryAssert, and ExpectedException attributes. Critical IssuesNone found ✅ Previous Review StatusThe author (thomhurst) conducted thorough self-review and addressed all critical issues:
TUnit Rules Compliance✅ Dual-Mode: N/A - No changes to core engine metadata collection Code Quality ObservationsStrengths:
Minor notes (not blocking):
Verdict✅ APPROVE - Excellent implementation. All critical issues have been resolved, TUnit rules are followed, and the code is production-ready. The PR successfully adds comprehensive NUnit migration support with correct semantics. |
Summary
Changes
FileAssert Support
FileAssert.Exists(path)→Assert.That(File.Exists(path)).IsTrue()FileAssert.DoesNotExist(path)→Assert.That(File.Exists(path)).IsFalse()FileAssert.AreEqual(expected, actual)→Assert.That(File.ReadAllBytes(actual)).IsEquivalentTo(File.ReadAllBytes(expected))FileAssert.AreNotEqual(expected, actual)→Assert.That(File.ReadAllBytes(actual)).IsNotEquivalentTo(File.ReadAllBytes(expected))DirectoryAssert Support
DirectoryAssert.Exists(path)→Assert.That(Directory.Exists(path)).IsTrue()DirectoryAssert.DoesNotExist(path)→Assert.That(Directory.Exists(path)).IsFalse()DirectoryAssert.AreEqual/AreNotEqual→ Directory content comparisonExpectedException Support
[ExpectedException(typeof(T))]attribute toAssert.ThrowsAsync<T>()wrapperasynclambda when await expressions are present)Test plan
🤖 Generated with Claude Code