I have a project full of unit tests which is using the new VS2017 project format. The project targets 'net461'. This is significant because while this is a supported setup in VS2017, it is worth noting that the .NET Framework 4.6.1 project templates in VS2017 still use the old project format. Therefore, this exact setup has a high likelihood of not having been well-trodden compared to other arrangements, such as a .NET Core project using the new VS2017 project format, or a .NET Framework project using the old project format.
Here's what a simple project looks like that can repro the issue I'm about to describe:
<Project ToolsVersion="15.0" Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net461</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="NUnit" Version="3.10.1" />
</ItemGroup>
</Project>
For about half of my team (the other half is unaffected), when they attempt to run the NUnit3 tests using resharper in VS2017, they get a NullReferenceException
in JetBrains.ReSharper.UnitTestProvider.nUnit.v30.NUnitServiceProvider.GetRunStrategy(IUnitTestElement element)
. The same issue occurs in the v26 nUnit provider; I originally had attempted to upgrade to a newer version of NUnit to see if it would resolve the issue, but it did not resolve it.
This is the decompiled code where the exception is thrown. I don't know until Monday when I can debug the exception which line is the offender:
public IUnitTestRunStrategy GetRunStrategy(IUnitTestElement element)
{
IProject project = element.Id.Project;
if (!project.IsDotNetCoreProject())
return (IUnitTestRunStrategy) this.myRunStrategy;
if (this.myDotNetCoreSdkResolver.GetVersion(project) < ImportantSdkVersions.VsTestVersion)
return (IUnitTestRunStrategy) this.myDotNetTestRunStrategy ?? (IUnitTestRunStrategy) (this.myDotNetTestRunStrategy = (DotNetTestRunStrategy) this.mySolution.GetComponent<NUnitDotNetTestRunStrategy>());
if (element.Id.TargetFrameworkId.IsNetCoreApp)
return (IUnitTestRunStrategy) this.myDotNetVsTestRunStrategy ?? (IUnitTestRunStrategy) (this.myDotNetVsTestRunStrategy = (DotNetVsTestRunStrategy) this.mySolution.GetComponent<NUnitDotNetVsTestRunStrategy>());
return (IUnitTestRunStrategy) this.myRunStrategy;
}
This method has some code that cares whether the project is a .NET Core project, and it seemed a little bit suspicious to me that the crash would happen specifically in a method that cares if the project is a .NET Core project, because it is not a .NET Core project, and I had a hunch that maybe the project was being erroneously treated as a .NET Core project, and maybe that was causing issues.
I'll have to wait until Monday to get access to one of my colleague's machines that is actually exhibiting the crash, but in the meantime, I used dotPeek decompiler and symbol server to debug this on my machine which is not exhibiting the crash, and I observed that the project is indeed erroneously being considered a .NET Core project.
The offending method calls JetBrains.ProjectModel.IsDotNetCoreProject
, which returns true because DotNetCoreSDK
is not null in the ProjectImpl.ProjectProperties
. I traced the fact that DotNetCoreSDK
is not null back to JetBrains.VsIntegration.ProjectDocuments.Projects.Builder.GetOrCreateDescriptor
. This method calls this.NeedsDotNetCoreSDK
, which erroneously returns true. The expression in that method:
return projectTypeGuid == CSharpProjectPropertiesFactory.ProjectKProjectTypeGuid || !projectLocation.ExtensionWithDot.Equals(".vcxproj", StringComparison.OrdinalIgnoreCase) && TargetFrameworkIdInProjectFile.Instance.GetTargetFrameworkIds(projectLocation).Any<TargetFrameworkId>();
evaluates to:
false || true && true
where the target framework ids result is just one entry, '.NETFramework,Version=v4.6.1'.
I believe the last part of this expression is the bug, because it is treating any target framework id having been set in the project as indicative of the project being a .NET Core project, which is not true, because the new VS2017 project format can be used for .NET Framework projects as well.
I created a .NET Framework project with the legacy project format and checked out how this same code played out, and indeed there are no target framework ids, and so that expression evaluates to 'false', confirming that this is where things are going awry.
At the end of the day, I don't yet know if this is what is causing the crash half my team is experiencing, and it will be Monday before I can get a debugger on the actual exception being thrown. But in the meantime, this seems like enough information to show that something isn't right here.
This issue was reproduced on multiple versions of ReSharper ultimate 2017, and the code snippets from above are decompiled from the 2017.3.5 (2018-03-23) build.