Skip to content
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

PlatformNotSupportedException when testing a .NET Standard assembly with reference to System.Data.SqlClient #1282

Open
dnedkovhedgeserv opened this issue Dec 20, 2022 · 15 comments

Comments

@dnedkovhedgeserv
Copy link

dnedkovhedgeserv commented Dec 20, 2022

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:

  1. 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
  2. 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
  3. Build the solution
  4. (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"
  5. Navigate to {project_directory}/bin/debug/net6
  6. Using powershell invoke dotnet nunit {PROJECT_NAME}.Tests.dll
  7. 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?

@manfred-brands
Copy link
Member

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.

@dnedkovhedgeserv
Copy link
Author

@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...

@manfred-brands
Copy link
Member

You can output to nunit3 format with dotnet test as well. See nunit/nunit3-vs-adapter#1114

@dnedkovhedgeserv
Copy link
Author

Yep that should do it. I still think the original issue might be worth looking into, though.
Anyways, thanks for your assistance!

@manfred-brands
Copy link
Member

I agree. We need to compare the loading in nunit.console with the one from the adapter.

@stevenaw
Copy link
Member

stevenaw commented Dec 20, 2022

This could be related to or duplicating #1269

@SimonCropp
Copy link

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

@SimonCropp
Copy link

Debug\net6.0\Microsoft.Data.SqlClient.dll

is always the stub assembly that throws for most methods with NotSupported

@SimonCropp
Copy link

@SimonCropp
Copy link

is TestAssemblyLoadContext missing a call to TestAssemblyResolver.OnResolving so it can interrogate _dependencyContext.RuntimeLibraries ?

@CharliePoole
Copy link
Collaborator

@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.

@SimonCropp
Copy link

@CharliePoole i debugged into it and OnResolving was never hit for the sql dll

@CharliePoole
Copy link
Collaborator

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?

@aw2003
Copy link

aw2003 commented Mar 13, 2023

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

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.

@indy-singh
Copy link

If it helps other people arriving from Google; upgrading from 3.16.2 to 3.16.3 fixed this for us.

Cheers,
Indy

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants