nunit-console
nunit-console copied to clipboard
PlatformNotSupportedException when testing a .NET Standard assembly with reference to System.Data.SqlClient
Hello, As the title suggests we faced an issue when trying to unit test a .NET Standard assembly which transitively references System.Data.SqlClient.
Steps to reproduce:
- Create a .NET Standard library which uses System.Data.SqlClient 1.1 Create an empty solution and a new .NET Standard project using Visual Studio 1.2 Add a package reference to System.Data.SqlClient, version 4.6.1 in the project's .csproj file 1.3 Create a public method which simply opens a new SqlConnection to a local database
- Create a .NET 6 NUnit test project
2.1 Create a .NET 6 project using visual studio
2.2 Add a package a package reference to NUnit, version 3.7.1, Microsoft.NET.Test.SDK, version 17.4.0 and NUnit3TestAdapter, version 3.15.1
2.3 Add a project reference to the the project created in 1.
2.4 Create a test method, mark it with a
[Test]attribute and invoke the method from the .NET Standard library created in 1.3 - Build the solution
- (Optional if already installed)Install Nunit.TestRunner.Core as a tool with the command
dotnet tool install --local "NUnit.ConsoleRunner.NetCore" --version "3.16.1-dev00002" - Navigate to {project_directory}/bin/debug/net6
- Using powershell invoke
dotnet nunit {PROJECT_NAME}.Tests.dll - Observe that the tests fail with the following exception:
System.PlatformNotSupportedException : System.Data.SqlClient is not supported on this platform.
at System.Data.SqlClient.SqlConnection..ctor(String connectionString)
at AssemblyUnderTest.LibMethods.GetDbStrings()...
Probable cause: The .NET Standard 2.0 assembly System.Data.SqlClient which is built and located directly under /bin is simply a placeholder assembly which throws PlatformNotSupportedException in every public method that it exposes. The reason for this is that the implementation of System.Data.SqlClient is platform-specific and because of that, the runtime must locate this platform-specific implementation during the runtime of the program. This is usually not a problem, since this information is located in the {project_directory}.Tests.deps.json file, which the runtime uses to locate the concrete implementation of System.Data.SqlClient.
If we add the following lines anywhere in our test:
var loadedDeps = AppContext.GetData("APP_CONTEXT_DEPS_FILES");
TestContext.Out.WriteLine($"LOADED DEPS:\r\n{loadedDeps}");
we can see that the output when the test is ran via Visual Studio and with the nunit console is different.
For Visual Studio it will be similar to the following:
LOADED DEPS:
C:\\Users\\USER\\source\\repos\\TestProject.Tests\\TestProject.Tests\\bin\\Debug\\net6.0\\TestProject.Tests.deps.json;
C:\\Program Files\\dotnet\\shared\\Microsoft.NETCore.App\\6.0.8\\Microsoft.NETCore.App.deps.json
For the nunit console it will be similar to:
LOADED DEPS:
C:\Users\USER\.nuget\packages\nunit.consolerunner.netcore\3.16.1-dev00002\tools\net6.0\any\nunit3-netcore-console.deps.json;
C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App\6.0.8\Microsoft.AspNetCore.App.deps.json;
C:\Program Files\dotnet\shared\Microsoft.NETCore.App\6.0.8\Microsoft.NETCore.App.deps.json
As we can see in the second output there's no reference to TestProject.Tests.deps.json which actually contains the information on how to load the correct version of the System.Data.SqlClient assembly during runtime
Is there a workaround around this issue?
As you reference the Nunit3Adapter you don't need to install the nunit.console tool and can use dotnet test.
Can you check if that does that load the correct DLL?
It could be the same issue as the adapter and the console runner share code, but there might be some differences.
@manfred-brands Just tested running dotnet test and the tests actually pass! However we would much prefer to use the nunit console runner to generate the tests results as it can write in the nunit3 format, which we actually need for our CICD pipeline...
You can output to nunit3 format with dotnet test as well. See https://github.com/nunit/nunit3-vs-adapter/issues/1114
Yep that should do it. I still think the original issue might be worth looking into, though. Anyways, thanks for your assistance!
I agree. We need to compare the loading in nunit.console with the one from the adapter.
This could be related to or duplicating #1269
interesting.
[Test]
public void Test1()
{
var assemblyLocation = typeof(SqlConnection).Assembly.Location;
var c = new SqlConnection();
Assert.Pass();
}
when targeting NUnit.Engine 3.15.2 then assemblyLocation is
C:\Code\nunit-4279-repro\TestProject1\bin\Debug\net6.0\runtimes\win\lib\net6.0\Microsoft.Data.SqlClient.dll
when targeting NUnit.Engine 3.16.2 then assemblyLocation is
C:\Code\nunit-4279-repro\TestProject1\bin\Debug\net6.0\Microsoft.Data.SqlClient.dll
Debug\net6.0\Microsoft.Data.SqlClient.dll
is always the stub assembly that throws for most methods with NotSupported
perhaps the problem is a combo of this https://github.com/dotnet/SqlClient/issues/1687 and the fact that we now use AssemblyLoadContext https://github.com/nunit/nunit-console/blob/76330acce7d36db94e3acc03667e608b0f4858cc/src/NUnitEngine/nunit.engine.core/Internal/TestAssemblyLoadContext.cs
is TestAssemblyLoadContext missing a call to TestAssemblyResolver.OnResolving so it can interrogate _dependencyContext.RuntimeLibraries ?
@SimonCropp
TestAssemblyResolver, sets the Resolving event of the load context to OnResolving in its constructor.
Someone with in-depth knowledge of SqlClient will probably need to look at this.
@CharliePoole i debugged into it and OnResolving was never hit for the sql dll
I'm guessing that's because it's in the base directory, but I'm really not sure. Can you see what happens if you remove it at execution time?
interesting.
[Test] public void Test1() { var assemblyLocation = typeof(SqlConnection).Assembly.Location; var c = new SqlConnection(); Assert.Pass(); }when targeting NUnit.Engine 3.15.2 then
assemblyLocationisC:\Code\nunit-4279-repro\TestProject1\bin\Debug\net6.0\runtimes\win\lib\net6.0\Microsoft.Data.SqlClient.dllwhen targeting NUnit.Engine 3.16.2 then
assemblyLocationisC:\Code\nunit-4279-repro\TestProject1\bin\Debug\net6.0\Microsoft.Data.SqlClient.dll
I'm seeing exactly this problem attempting to use System.IO.Ports (I know....) in a .Net 7.0 assembly with the NUnit3 console runner. The windows runtime assembly is never loaded, and we get the "System.PlatformNotSupportedException : System.IO.Ports is currently only supported on Windows." error from the stub assembly.
If it helps other people arriving from Google; upgrading from 3.16.2 to 3.16.3 fixed this for us.
Cheers, Indy