Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

System.MissingMethodException Unit Test

Having what appears to be runtime issues with Project References in Visual Studio 2017 Test Runner. The Unit Test CSPROJ builds just fine with TargetFramework=net47, but at execution time we get the following message from MSTEST or XUNIT. Using Microsoft.NET.Test.Sdk v15.0.0.

Test Execution Error (x86): Serilog Seq Extension

System.MissingMethodException : Method not found: 'Serilog.LoggerConfiguration Serilog.SeqLoggerConfigurationExtensions.Seq(Serilog.Configuration.LoggerSinkConfiguration, System.String, Serilog.Events.LogEventLevel, Int32, System.Nullable1<System.TimeSpan>, System.String, System.String, System.Nullable1, System.Nullable1<Int64>, Serilog.Core.LoggingLevelSwitch, System.Net.Http.HttpMessageHandler, System.Nullable1, Boolean, Int32)'.

Unit Test Example - Serilog

[Fact]
public void TestMethod1()
{
    LoggerConfiguration loggerConfig = new LoggerConfiguration();    
    loggerConfig.MinimumLevel.Debug();                    
    loggerConfig.WriteTo.LiterateConsole();    
    loggerConfig.WriteTo.Seq("http://localhost:65454");    
}

If we reference net462 projects, we get the same result so we believe it is related to VS 2017, not .NET Framework version. We have never seen this error with VS 2015. Seems like there is an issue loading DLL extensions with optional parameters / matching signatures, etc. The method clearly exists or it wouldn't compile - why at runtime is this crashing out?

If I just use local nuget packages it works fine - this only seems to be a problem when referencing any Projects via ProjectReference in .NET Core CSPROJ. It doesn't seem to handle the dependency tree properly.

Another example using KeyVault where VS Test Runner cannot find the extension methods properly...

Test Execution Error (x86): KeyVault Extension

Message: System.MissingMethodException : Method not found: 'Void Microsoft.Azure.KeyVault.KeyVaultClient..ctor(AuthenticationCallback, System.Net.Http.DelegatingHandler[])'.

Unit Test Example - KeyVault

[Fact]
public void TestMethod1()
{
    KeyVaultClient _kvClient = new KeyVaultClient(new AuthenticationCallback(getKeyVaultToken));
}

private static async Task<string> getKeyVaultToken(string authority, string resource, string scope)
{
    var authContext = new AuthenticationContext(authority);
    ClientCredential clientCred = new ClientCredential("test", "account");
    AuthenticationResult result = authContext.AcquireTokenAsync(resource, clientCred).Result;

    if (result == null)
        throw new InvalidOperationException("Failed to obtain the JWT token");

    return result.AccessToken;
}
like image 380
SliverNinja - MSFT Avatar asked May 09 '17 15:05

SliverNinja - MSFT


1 Answers

Discovered this odd issue with dotnet test is two-fold. After running dotnet test --diag and reviewing the output, it led me to realize there are newer releases of Microsoft.NET.Test.Sdk which version 15.0.0 was masking the real problem. Once I upgraded the nuget to 15.3.0-preview-20170502-03, a different exception appeared.

Error Source: Microsoft.Rest.ClientRuntime

System.TypeLoadException: 'Inheritance security rules violated by type: 'System.Net.Http.WebRequestHandler'. Derived types must either match the security accessibility of the base type or be less accessible.'

Now this is interesting - the MissingMethodException was masking the real problem which was buried in System.Net.Http. The second realization is that this base library has a bug which prevents the type from being loaded. Once I nuget updated System.Net.Http to version 4.3.1, the issue went away and my Project References started working again.

Conclusion

Update Microsoft.NET.Test.SDK to latest preview and System.Net.Http to latest version to get past the weird MissingMethodException with dotnet test. You can track the open issue on github here.

Option #2 - Excluding Package Reference Assets

For latest VS 2017 CSPROJ formats - the following config also fixes this as it supresses copying System.Net.Http to the Build Output path which by default loads the GAC'd version 4.0.0.0.

<PackageReference Include="System.Net.Http" Version="4.3.1">
  <ExcludeAssets>All</ExcludeAssets>
</PackageReference>

Option #3 - Assembly Binding Redirect

dotnet core will follow any runtime binding redirects you place in your app.config, so any nuget dependencies you have to System.Net.Http to version 4.1.*, you can redirect to the latest version or revert to the last stable version 4.0.

<configuration>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <!-- Another PackageReference dependency binds to 4.1.1 which is busted, we leverage .NET Core redirection and revert to CLR 4.0.0 -->
      <dependentAssembly>
        <assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
        <bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="4.0.0.0"/>
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>
like image 187
SliverNinja - MSFT Avatar answered Nov 14 '22 12:11

SliverNinja - MSFT