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
xUnit.net support broken with xUnit.net 2.5 #1099
Comments
For what it's worth, I approached submitting a PR to fix this, but ran across several stumbling blocks. First, I can build from the command line, but not inside Visual Studio 2022 17.6.2: Second, I was unable to locate where Third, when trying to shift This is the diff to make it "work": diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EmptyWithMessageException.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EmptyWithMessageException.cs
index d91b951..2fa7297 100644
--- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EmptyWithMessageException.cs
+++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EmptyWithMessageException.cs
@@ -8,7 +8,7 @@ using Xunit.Sdk;
namespace Microsoft.CodeAnalysis.Testing.Verifiers
{
- public class EmptyWithMessageException : EmptyException
+ internal class EmptyWithMessageException : EmptyException
{
public EmptyWithMessageException(IEnumerable collection, string userMessage)
: base(collection)
diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EqualWithMessageException.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EqualWithMessageException.cs
index 3a14e78..e2d10c7 100644
--- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EqualWithMessageException.cs
+++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/EqualWithMessageException.cs
@@ -7,7 +7,7 @@ using Xunit.Sdk;
namespace Microsoft.CodeAnalysis.Testing.Verifiers
{
- public class EqualWithMessageException : EqualException
+ internal class EqualWithMessageException : EqualException
{
public EqualWithMessageException(object? expected, object? actual, string userMessage)
: base(expected, actual)
diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit.csproj b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit.csproj
index 2947cb5..134c771 100644
--- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit.csproj
+++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit.csproj
@@ -4,6 +4,23 @@
<TargetFrameworks>$(TestingLibraryTargetFrameworks)</TargetFrameworks>
<AssemblyName>Microsoft.CodeAnalysis.Testing.Verifiers.XUnit</AssemblyName>
<RootNamespace>Microsoft.CodeAnalysis.Testing.Verifiers</RootNamespace>
+ <DefineConstants>$(DefineConstants);XUNIT_NULLABLE;XUNIT_VISIBILITY_INTERNAL</DefineConstants>
+ <NoWarn>
+ $(NoWarn);
+ AD0001;
+ CS8604;
+ IDE0055;
+ RS0016; RS0027;
+ SA1003; SA1009; SA1027;
+ SA1108; SA1111; SA1117; SA1121; SA1122; SA1127; SA1128;
+ SA1206;
+ SA1300; SA1310; SA1313; SA1314;
+ SA1400; SA1412; SA1413;
+ SA1502; SA1503; SA1505; SA1507; SA1513; SA1515; SA1519; SA1520;
+ SA1611; SA1614; SA1615; SA1616; SA1617; SA1618; SA1623; SA1633; SA1642; SA1648; SA1649;
+ SX1101;
+ SX1309
+ </NoWarn>
</PropertyGroup>
<PropertyGroup>
@@ -14,7 +31,7 @@
</PropertyGroup>
<ItemGroup>
- <PackageReference Include="xunit.assert" Version="2.3.0" />
+ <PackageReference Include="xunit.assert.source" Version="2.4.2" />
</ItemGroup>
<ItemGroup>
diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/NotEmptyWithMessageException.cs b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/NotEmptyWithMessageException.cs
index 9204a8b..f5b8e83 100644
--- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/NotEmptyWithMessageException.cs
+++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/NotEmptyWithMessageException.cs
@@ -7,7 +7,7 @@ using Xunit.Sdk;
namespace Microsoft.CodeAnalysis.Testing.Verifiers
{
- public class NotEmptyWithMessageException : NotEmptyException
+ internal class NotEmptyWithMessageException : NotEmptyException
{
public NotEmptyWithMessageException(string userMessage)
{
diff --git a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/PublicAPI.Unshipped.txt b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/PublicAPI.Unshipped.txt
index 2745b01..3b935e8 100644
--- a/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/PublicAPI.Unshipped.txt
+++ b/src/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit/PublicAPI.Unshipped.txt
@@ -1,10 +1,3 @@
-Microsoft.CodeAnalysis.Testing.Verifiers.EmptyWithMessageException
-Microsoft.CodeAnalysis.Testing.Verifiers.EmptyWithMessageException.EmptyWithMessageException(System.Collections.IEnumerable collection, string userMessage) -> void
-Microsoft.CodeAnalysis.Testing.Verifiers.EqualWithMessageException
-Microsoft.CodeAnalysis.Testing.Verifiers.EqualWithMessageException.EqualWithMessageException(object expected, object actual, string userMessage) -> void
-Microsoft.CodeAnalysis.Testing.Verifiers.EqualWithMessageException.EqualWithMessageException(string expected, string actual, int expectedIndex, int actualIndex, string userMessage) -> void
-Microsoft.CodeAnalysis.Testing.Verifiers.NotEmptyWithMessageException
-Microsoft.CodeAnalysis.Testing.Verifiers.NotEmptyWithMessageException.NotEmptyWithMessageException(string userMessage) -> void
Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier
Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.Context.get -> System.Collections.Immutable.ImmutableStack<string>
Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.XUnitVerifier() -> void
@@ -19,6 +12,3 @@ virtual Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.NotEmpty<T>(strin
virtual Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.PushContext(string context) -> Microsoft.CodeAnalysis.Testing.IVerifier
virtual Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.SequenceEqual<T>(System.Collections.Generic.IEnumerable<T> expected, System.Collections.Generic.IEnumerable<T> actual, System.Collections.Generic.IEqualityComparer<T> equalityComparer = null, string message = null) -> void
virtual Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier.True(bool assert, string message = null) -> void
-override Microsoft.CodeAnalysis.Testing.Verifiers.EmptyWithMessageException.Message.get -> string
-override Microsoft.CodeAnalysis.Testing.Verifiers.EqualWithMessageException.Message.get -> string
-override Microsoft.CodeAnalysis.Testing.Verifiers.NotEmptyWithMessageException.Message.get -> string The warning disablement is extensive, and it didn't feel appropriate to PR with that in place; it would make more sense to figure out how to exclude external code (via a source-based NuGet package) from code analysis. I was unable to figure that out in the short time I gave myself. 😁 There are test failures here, because the unit tests you've put in place take hard dependencies on the exception types (which are no longer type-matched, since you're throwing internal versions). Here's one example (of the 26 failures):
The simple fix here would be to stop the strong type testing (and remove all the checks against properties you shouldn't really care about, like diff --git a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit.UnitTests/XUnitVerifierTests.cs b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit.UnitTests/XUnitVerifierTests.cs
index 849e62c..9742a2e 100644
--- a/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit.UnitTests/XUnitVerifierTests.cs
+++ b/tests/Microsoft.CodeAnalysis.Testing/Microsoft.CodeAnalysis.Testing.Verifiers.XUnit.UnitTests/XUnitVerifierTests.cs
@@ -15,7 +15,7 @@ namespace Microsoft.CodeAnalysis.Testing.Verifiers
{
var actual = new int[1];
var verifier = new XUnitVerifier();
- var exception = Assert.ThrowsAny<EmptyException>(() => verifier.Empty("someCollectionName", actual));
+ var exception = Assert.ThrowsAny<Exception>(() => verifier.Empty("someCollectionName", actual));
Assert.Equal($"'someCollectionName' is not empty{Environment.NewLine}Assert.Empty() Failure{Environment.NewLine}Expected: <empty>{Environment.NewLine}Actual: [0]", exception.Message);
}
Also, one of your analyzers was repeatedly throwing an exception, which is why I had to also exclude AD0001:
|
@bradwilson The expectation is for xunit.assert to retain binary compatibility of the public API surface for 2.x releases, where x >= 4. If xunit.assert does not provide binary compatibility that works with binding redirection, the SDK provides generic packages (without the |
@bradwilson if you have any questions on the analyzer project update feel free to reach out |
@sharwell We are breaking binary compatibility with 2.5. |
I think the only significant impact of that will be users will need to provide their own implementation of XUnitVerifier when updating to 2.5. Is there a need to break compatibility in 2.5 knowing 3.0 is on the horizon and has more flexibility to break things? |
The same assertion library is shipped in 2.5 that is currently part of 3.0. Even if we didn't ship this with 2.5 (which, to be clear, is not an option), you'd still be broken for anybody who wanted to use 3.0. Your binary dependency is the problem, regardless of which exact version breaks you. |
Right, but the binary dependency is optional (removed by switching from Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.Xunit → Microsoft.CodeAnalysis.CSharp.CodeFix.Testing). I expect we'll come up with a more concrete plan after 2.5 and/or 3.0 is released in a stable version. |
…tion library (as source)
I've used this to work around the issue: xunit/xunit.analyzers@1b8cf8a?w=1 If I'm the only consumer of |
I've also fixed the issue where xunit.analyzers was triggering xUnit2007 and xUnit2015 on the assert source itself (examples of "I have to break the rules, but you shouldn't" 😂). xunit/assert.xunit@205442f I still am experiencing occasional AD0001 (with NullRef exceptions) from
|
A full stack trace and context information is provided in the Visual Studio error list (design time errors) or in the .binlog (command line builds).
This analyzer is disabled by default starting with dotnet/roslyn-analyzers#6705. A stricter/faster analyzer will be provided instead that requires analyzers be implemented in an assembly that does not reference Microsoft.CodeAnalysis.Workspaces, and code fixes are implemented in a separate assembly (typically shipping in the same package). |
@sharwell Thanks, reported dotnet/roslyn-analyzers#6708 |
FWIW, the problematic assembly is the test assembly, not either of the production assemblies. It seems like they get the analyzers run because of the project reference from the unit tests to the production assemblies. There's no reason they should be run at all, but I don't think I can disable it (short of just turning off every analyzer in the test project via NoWarn or .editorconfig). |
FYI, 2.5.0 shipped today. https://fosstodon.org/@xunit/110669666679697253 |
.NET, degrade XUnit until dotnet/roslyn-sdk#1099 has been fixed
I'm using |
@b-straub Given the compiler issues I ran across, switching to the source-based version of our assertion library will not be trivial for them (because our source does not conform to their source rules, and I have no idea whether it's even possible to exclude source-via-NuGet from getting the rules run against it). As such, I am not able to effectively create a PR. I don't have a good recommendation, either. If their rules cannot be excluded from xUnit.net's source, then likely they'll need to stop using our assertion library outright (or perhaps fork it, and rewrite the bits they need to conform to their coding style). |
The xUnit.net source-only nuget package should probably have |
@Shane32 Are the rules about this written down somewhere? I wouldn't mind modifying the source to add headers to fix the issue if that's what it takes. It would certainly make it more widely usable. |
I don't know. See: DotNetAnalyzers/StyleCopAnalyzers#3299 |
It does seem to make sense, or else source generation (such as System.Text.Json AOT support) would likely break compilation for many repositories where the generated files did not match the rules configured for the project. |
@gao-artur That sounds like a simpler solution than adding the headers before packing. 😄 |
By applying the changes suggested by @gao-artur (and fixing up a couple issues that popped up as a result), I'm able to get the following changes to build There remains compiler errors from
Fixing up the filenames causes a host of incompatibility errors from the conflicts from the
It's at this point that I bow out. The build magic that's being applied here (starting with being unable to work inside of Visual Studio, through "magic" imports and variables like I will leave the fork available, but I will not be opening a PR to fix this issue. Although the Roslyn team should wait for a 2.5.1 RTM before doing any final merges, I strongly urge you to attempt to validate your fix with a pre-release build, in the event that you find any further issues that I hadn't uncovered which may require any further changes to xUnit.net. As always, there are no guarantee of any future 2.x releases for xUnit.net, so any breaking issues discovered after 2.5.1 ships may not be fixed for the 2.x tree. The time to verify this approach is now, or else you risk being stranded indefinitely on xUnit.net <= 2.4.2 (forcing any consumers of |
dotnet/roslyn-sdk#1099 still not fixed -> xunit 2.4.2
I'm using |
@elringus you can also switch to |
@sharwell This works, thank you! |
That workaround unblocked me on xunit 2.5.1 as well. |
i was also bit by this. fix mentioned by sharwell also works for me. |
Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.Xunit brings in an older xunit reference that is not compatible in some cases, we've hit issues with it while upgrading our xunit reference in the past. As mentioned in dotnet/roslyn-sdk#1099 (comment) we can use the generic testing package instead. The only difference is the exception type thrown when an assert fails, e.g. the test throws `InvalidOperationException` instead of `Xunit.Sdk.EqualException` but we still get actual vs. expected exception messages so this is enough for us.
Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.Xunit brings in an older xunit reference that is not compatible in some cases, we've hit issues with it while upgrading our xunit reference in the past. As mentioned in dotnet/roslyn-sdk#1099 (comment) we can use the generic testing package instead. The only difference is the exception type thrown when an assert fails, e.g. the test throws `InvalidOperationException` instead of `Xunit.Sdk.EqualException` but we still get actual vs. expected exception messages so this is enough for us.
Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.Xunit brings in an older xunit reference that is not compatible in some cases, we've hit issues with it while upgrading our xunit reference in the past. As mentioned in dotnet/roslyn-sdk#1099 (comment) we can use the generic testing package instead. The only difference is the exception type thrown when an assert fails, e.g. the test throws `InvalidOperationException` instead of `Xunit.Sdk.EqualException` but we still get actual vs. expected exception messages so this is enough for us.
What if I cannot follow the workaround because I basically have https://github.dev/dotnet/roslyn-sdk/samples/CSharp/Analyzers/Analyzers.Test/Tests/CodeBlockAnalyzerUnitTests.cs in my codebase that is crashing with:
csproj: <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.1"/>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.1"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.6.6" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference> |
|
@bradwilson I was reviewing today, and was not able to find a way to make xunit 2.5 compatible with our goals. Would it be possible to re-add the following method APIs to restore binary compatibility? public class EmptyException
{
[Obsolete($"Use '{nameof(ForNonEmptyCollection)}' instead.")]
public EmptyException(IEnumerable collection)
: this(
string.Format(
CultureInfo.CurrentCulture,
"Assert.Empty() Failure: Collection was not empty{0}Collection: {1}",
Environment.NewLine,
CollectionTracker<object?>.FormatStart(Assert.GuardArgumentNotNull(nameof(collection), collection.Cast<object?>())))
{
}
}
public class NotEmptyException
{
[Obsolete($"Use '{nameof(ForNonEmptyCollection)}' instead.")]
public NotEmptyException()
: this("Assert.NotEmpty() Failure: Collection was empty")
{
}
}
public class EqualException
{
[Obsolete($"Use '{nameof(ForMismatchedValues)}' instead.")]
public EqualException(object? expected, object? actual)
: this(.. /* refactor ForMismatchedValuesWithError string generation */)
{
}
[Obsolete($"Use '{nameof(ForMismatchedStrings)}' instead.")]
public EqualException(string? expected, string? actual, int expectedIndex, int actualIndex)
: this(.. /* refactor ForMismatchedStrings string generation */)
{
}
}
public class XunitException
{
[Obsolete("No longer used.")]
public string? UserMessage { get; protected set; }
} |
@sharwell You should not be taking a binary dependency against our assertion library. Either stop using our assertion exceptions, or import our assertions as source so you don't have a binary dependency. |
Use this as a starting point: #1099 (comment) |
Both of these options will penalize users by making the framework not throw |
@sharwell The only code that should care what exceptions you throw is your tests, not everybody else's tests. Anybody who's writing a test to catch and verify |
If I'm being blunt: the I haven't looked at the MSTest and NUnit variants because I don't honestly care about what you did there, but I suspect they're just as empty as the Xunit variants are. If I had my way, I'd deprecate all the test-framework-specific versions and point people to the base project. jm2c |
OK, that was the plan if we couldn't figure out the compat issue. |
In upgrading the xUnit.net analyzers project to use a prerelease 2.5.0 build, I've determined that
Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit
(and/or some of its dependencies) are broken when my code uses 2.5.Any verifier which fails breaks because of a breaking binary dependency on several of the xUnit.net exception classes. (The assertion library, including all the exceptions, has been overhauled for v2 2.5 and v3.) The source of the problem is that you're taking a binary dependency on
xunit.assert
, which is locking consumers into your version choice when breaking changes are made to anything you consume.This behavior isn't appropriate, in my view. Your dependency in essence locks users down to only being able to use "compatible" versions of xUnit.net. This is true for any binary dependency you take outside of the core framework: if you ship a hard dependency on a binary that someone else may want to use in their tests, then you are potentially locking them down to only being able to use a version that's compatible with your choice. I ended up removing all binary dependencies from xUnit.net v2 for exactly this reason; for us, the problematic library ended up being JSON.NET, and it broke JSON.NET when they wanted to test themselves with us. Even if we're one of the few or only consumers of this library, we're guaranteed to want to "move on" to newer versions of xUnit.net. 😄
Here is a quick example showing the problem. Given the follow code which should clearly fail:
This is the (expected) result using xUnit.net 2.4.2:
And this is the (incorrect) result using xUnit.net 2.5.0-pre.22:
I can think of at least two suggestions for potential paths forward.
1. Don't use
xunit.assert
at all.xUnit.net considers all exceptions to be failing tests. Don't consume the xUnit.net assertion library; instead, create your own exceptions with your own messages and throw those.
2. Use
xunit.assert.source
(or Git sub-module) instead.If you wish to consume the library but not ship a binary dependency on it, you can import the source version of our library and use that instead. The project is designed to be consumed in both of these fashions.
By default, importing the source (via NuGet or sub-module) will bring in all the code as public, but you can override that with
XUNIT_VISIBILITY_INTERNAL
. You can use pre-processor directives to influence which features are enabled or disabled. By leaving everything as internal, you will be able to use the existing assertions without republishing them into your API.The text was updated successfully, but these errors were encountered: