The setup is as follows:
a .NET Standard 2.0 library
a NUnit test project for it, targeting .NETFramework 4.6.1
Latest VS 2017, latest NUnit.
I've been working on the project on weekend from home, uploaded my work to git and today started working from work (I've already worked from both places before). Only now I found that something was wrong with the project (I don't remember very well what was wrong at the start, but it seems the problem was the same as I have now, described later).
After fiddling around to unrepairable state with it, I wholly deleted it and cloned the git repo anew.
The project compiles fine, but at runtime tests throw "Method not found" exception. A bit of poking around showed that the problem only manifests on one overload of the following method:
public static YNABClient GetInstance(HttpMessageHandler _handler)
{
if (instance is null)
{
instance = new YNABClient(_handler);
}
return instance;
}
public static YNABClient GetInstance() => GetInstance(new HttpClientHandler());
The one without parameters is fine, the one with is not. Deleting and adding library as a reference to tests, deleting and adding both test and library project. Other solutions for similar situations I found on the internet all pertain to ASP.NET MVC, which is not my case, though this question did lead me to checking overloads and finding that one of them actually works.
At home everything still works fine, though I have yet to try to delete and reinstall the project as I did at work. This leads to 2 possible sources for problems: environment, though I haven't managed to find a meaningful difference, or git, though I use a "stock" git ignore for VS (this one), so there shouldn't be problems there. The basic setup for my projects didn't change during weekend and worked before, so something broke from recent fiddling.
Also, if I add a console application(.Net Framework 4.6.1) to solution and try calling the problematic method from it, it actually works fine.
If that would help, my github for the project is here
I've been asked for calling examples in the comments. Basically, I have 2 Test Fixture classes with different setups - one for real API calling for ease of debugging actual use, and one with faking it, as per good test practices.
Works:
[OneTimeSetUp]
public void Setup()
{
ynabClient = YNABClient.GetInstance();
ynabClient.RefreshAccessToken(ApiKeys.AccessToken);
}
Throws exception:
[OneTimeSetUp]
public void Setup()
{
handler = new StubHandler();
ynabClient = YNABClient.GetInstance(handler);
}
Some poking around shows that my problem is quite likely related to System.Net.Http versioning discrepancy with .NET Framework and .NET Standard, that is quite a widespread problem if you google it. However none of the examples I dug up exhibit my particular symptoms, and I'm not yet sure what to do. And why everything works fine on my home PC.
The issue that you have is that your GetInstance method accepts HttpMessageHandler as parameter, and in your test you are passing HttpClientHandler object. So, you have declared one parameter, but provide different object when you call the method.
I got this error in my environment:
Severity Code Description Project File Line Suppression State
Error CS0433 The type 'HttpMessageHandler' exists in both
'System.Net.Http, Version=4.2.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a' and 'System.Net.Http,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'
Your YNABConnector is .NET Standard 2 but Unit test is 4.6.1, they are using different signature for same assembly.
Here is official document to test:
https://github.com/nunit/docs/wiki/.NET-Core-and-.NET-Standard
created a .NET Core Library(be aware that: cannot be .NET Standard library by above link),
copy your test code there,
follow the official document to add references,
your code works then, no errors.
But in your base code, I still prefer this code:
public class YNABClient
{
private static YNABClient instance;
private HttpClient client;
private HttpMessageHandler handler;
private YNABClient(HttpMessageHandler _handler = null)
{
handler = _handler ?? new HttpClientHandler();
client = new HttpClient(_handler);
}
public static YNABClient GetInstance(HttpMessageHandler _handler = null)
{
return instance ?? (instance = new YNABClient(_handler));
}
......
}
So, it turns out, as DongDong suggested, the problem is indeed with interfacing between .NET Framework and .NET Standard. The problem is not really that they're incompatible, or that Test project needed some additional dependencies, but that they're shipped with different versions of System.Net.Http.
The diagnostics were hindered by showing no visible errors. However, changing parameter types showed that indeed, the problem is only with classes from that namespace.
Another problem with diagnosing the issue was that the project works fine on some machines (my home PC and Daisy Shipton's from comments to the question).
However after determining the source of the problem I was able to google what problems exist with the library in the first place, and eventually found a trove of uncompatibility issues on .NET github.
I tried the solution used in those cases and added a "binding redirect" to a concrete version of the library, and after that it works fine. I added the redirect to app.config of my Tests project. For reference it looks like this:
<dependentAssembly>
<assemblyIdentity name="System.Net.Http" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-4.2.0.0" newVersion="4.2.0.0" />
</dependentAssembly>
I still have no understanding of why the project worked fine on some machines.
Related
Before I start, I've tried all suggestions from the following and none work:
Integration testing ASP.NET Core with .NET Framework - can't find deps.json
https://zimmergren.net/unable-to-find-deps-json-dotnet-azure-devops/
So I'm trying to write some integration tests for dotnet 6. However, my WebApplicationFactory throws the following error:
System.InvalidOperationException: Can't find
'/repos/subscription-info-api/tests/SubscriptionInfoApi.Tests.Integration/bin/Debug/net6.0/...
System.InvalidOperationException Can't find
'/repos/subscription-info-api/tests/SubscriptionInfoApi.Tests.Integration/bin/Debug/net6.0/testhost.deps.json'.
This file is required for functional tests to run properly. There
should be a copy of the file on your source project bin folder. If
that is not the case, make sure that the property
PreserveCompilationContext is set to true on your project file. E.g
'true'. For
functional tests to work they need to either run from the build output
folder or the testhost.deps.json file from your application's output
directory must be copied to the folder where the tests are running on.
A common cause for this error is having shadow copying enabled when
the tests run. at
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory1.EnsureDepsFile() at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory1.EnsureServer()
at
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory1.CreateDefaultClient(DelegatingHandler[] handlers) at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory1.CreateDefaultClient(Uri
baseAddress, DelegatingHandler[] handlers) at
Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory1.CreateClient(WebApplicationFactoryClientOptions options) at Microsoft.AspNetCore.Mvc.Testing.WebApplicationFactory1.CreateClient()
at SubscriptionInfoApi.Tests.Integration.UnitTest1.Test1() in
/repos/subscription-info-api/tests/SubscriptionInfoApi.Tests.Integration/UnitTest1.cs:line
14 at SubscriptionInfoApi.Tests.Integration.UnitTest1.Test1() in
/repos/subscription-info-api/tests/SubscriptionInfoApi.Tests.Integration/UnitTest1.cs:line
16 at
Xunit.Sdk.TestInvoker1.<>c__DisplayClass48_0.<<InvokeTestMethodAsync>b__1>d.MoveNext() in /_/src/xunit.execution/Sdk/Frameworks/Runners/TestInvoker.cs:line 264 --- End of stack trace from previous location --- at Xunit.Sdk.ExecutionTimer.AggregateAsync(Func1 asyncAction) in
//src/xunit.execution/Sdk/Frameworks/ExecutionTimer.cs:line 48 at
Xunit.Sdk.ExceptionAggregator.RunAsync(Func`1 code) in
//src/xunit.core/Sdk/ExceptionAggregator.cs:line 90
My actual test code is extremely simple:
[Fact]
public async Task Test1()
{
await using var app = new WebApplicationFactory<Program>();
using var client = app.CreateClient();
var res = await (await client.GetAsync("/alive-test")).Content.ReadAsStringAsync();
Assert.Equal("Alive!", res);
}
As per the suggestions, I've made sure I'm directly referencing Microsoft.AspNetCore.Mvc.Testing -> 6.0.0 in my integration tests project. I've also tried the various tweaks to the .csproj files that were suggested but nothing seems to be working.
I'm stuck for things to try to debug this further, any ideas?
You are probably targeting the wrong namespace for Program in your test file (like I was).
I had to add the following at the end of my Program.cs file (last line) to make it visible to my test projects needing it:
public partial class Program { }
An example can be found here: minimal api testing example
I had the same problem, although for an entirely different reason.
I am using .net 6 but I deliberately chose to use an implementation that actually has a Program.cs file.
When I copied the code from the official MS integration test guide, I let VS pull in all the dependencies. The dependency used to resolve the Program.cs was not my own (PersonalSite for the sake of this answer), but one of MS's own implementation:
A small error on my part, sure, but maybe I can help somebody out.
For those who actually need the partial class implementation gimmick, the MS integ test guide I linked lists guidelines to do just that.
FWIW there is another approach that works as well (which doesn't require modifying code).
In your app's .csproj file add the following (Assuming your test project is called 'Tests'):
<ItemGroup>
<InternalsVisibleTo Include="Tests" />
</ItemGroup>
This allows your Tests project to see your Program.cs file.
change your Program class access specifier in your api from internal to Public
I use a simple postsharp.config file :
<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.postsharp.org/1.0/configuration">
<Multicast xmlns:my="clr-namespace:ExceptionAutoSerializer.Aspects;assembly:ExceptionAutoSerializer">
<my:MethodBoundaryAspect AttributeTargetTypes="MyTopLevelNamespace.*" />
<my:MethodBoundaryAspect AttributeTargetMembers="*ctor*" AttributeExclude="true"/>
<my:MethodBoundaryAspect AttributeTargetMembers="get_*" AttributeExclude="true"/>
<my:MethodBoundaryAspect AttributeTargetMembers="set_*" AttributeExclude="true"/>
</Multicast>
</Project>
All my project in my solution are under the namespace MyTopLevelNamespace. And every single project in the solution has the aspect applied to it correctly except for my website project. I'm not familiar with the solution as I just got in the dev team.
All I know is that I would like to apply aspect to classes within this project and that postsharp seems to ignore that particular project. The config file is located in the src/ folder and should be applied to all project.
I've made sure the types I'm applying my aspect to are under the namespace specified in the config file and that it doesn't match any of the excluding patterns.
Did I provide enough information ? I'm not sure it is due to the project beeing a website project but I can't see anything else.
Edit: I've made sure I added the nuget package to the project. I also tried to manually add the aspect with an attribute to a specific method of this project and the aspect doesn't trigger.
Edit2: this is the method that I use to test:
[MethodBoundaryAspect]
public bool Foo(string bar1, string bar2)
{
// at runtime test contains indeed one attribute MethodBoundaryAspect
var test = this.GetType().GetMethod("ValidateUser").GetCustomAttributes(false);
//here the exception is caught higher up but the "onException" of my attribute doesn't trigger
throw new Exception("test exception");
}
and my postsharp aspect :
namespace ExceptionAutoSerializer.Aspects
{
[Serializable]
public class MethodBoundaryAspect : OnMethodBoundaryAspect
{
//[...]
public override void OnEntry(MethodExecutionArgs args)
{
//[...]
}
public override void OnSuccess(MethodExecutionArgs args)
{
//[...]
}
public override void OnException(MethodExecutionArgs args)
{
//[...]
}
}
}
According to an old answer of a developper at PostSharp Technologies :
PostSharp is (currently) integrated via MSBuild (as also mentioned by
John Saunders), which is not used by website projects.
While in it's core PostSharp is a command-line tool, it gets so much
information from MSBuild that it's quite hard to make it work
separately (and neither advised nor documented nor supported in the
first place). Daniel Balas
I didn't find any update on that subject for version older than 4.3. For 4.3 and earlier according to the previous answer and this documentation (p. 45) website project are not supported.
Edit: when questioning the previously quoted author about the current validity of they answer they responded this in comment :
#Amon For project-less websites (which IIRC are not creatable from the
UI in VS 2017 and later) it is still true. However, all web projects
that have csproj/vbproj (.NET or .NET Core) are working as expected
(this also didn't change since then). – Daniel Balas
I have a very simple WebAPI 2 controller running on .NET Framework 4.6.2, that looks like this:
[RoutePrefix("Invitations")]
public class InvitationsController : CqrsApiController
{
[HttpPost, Route("Clients/{id:long}/Actions/Cancel")]
public IHttpActionResult PostClientInvitationCancel(long id, [FromBody] ClientInvitationCancelCommand command)
{
Execute(command);
return SeeOther("Invitations/Clients/{0}", id);
}
}
and am trying to write an NUnit test for it, like this:
[TestFixture]
public class WhenExecutingAValidCommand
{
[Test]
public void ItShouldReturnARedirect()
{
var dispatcher = Substitute.For<ICqrsDispatcher>();
var urlHelper = Substitute.For<UrlHelper>();
urlHelper.Link(Arg.Any<string>(), Arg.Any<object>()).Returns("https://tempuri.org/");
var sut = new InvitationsController(dispatcher);
sut.Request = new HttpRequestMessage();
sut.Configuration = new HttpConfiguration();
sut.Url = urlHelper;
var response = sut.PostClientInvitationCancel(1, new ClientInvitationCancelCommand());
response.Should().BeOfType<SeeOtherRedirectResult>();
}
}
```
However, when I run the test, I get the following error:
System.MissingMethodException : Method not found: 'Void System.Web.Http.ApiController.set_Request(System.Net.Http.HttpRequestMessage)'.
at ApiProjectTests.InvitationsControllerTests.WhenExecutingAValidCommand.ItShouldReturnARedirect()
The same code seems to work fine in similar projects based on .NET Framework 4.5.1, so I'm wondering if there's some sort of DLL hell going on here. System.Web.Http is using Microsoft.AspNet.WebApi.Core.5.2.3, whereas System.Net.Http is coming from the GAC (well, C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\Microsoft\Microsoft.NET.Build.Extensions\net461\lib\System.Net.Http.dll to be more precise).
Update: if I try to debug into the unit test, the error occurs before I even enter the method. So although VS2017 compiles the tests just fine, when the test runner fires up, then everything falls apart. Sounds more like DLL hell to me.
Update 2: if I comment out the setting of the request, then I can debug into the test method. If I then put in a breakpoint, and then use the Immediate window to directly set the request property, it works, and there is no Method not found error. I also disabled Resharper and used VS2017's Test Explorer to run the tests, in case R# was caching something, but it made no difference.
It looks like my problem is indeed DLL hell, more specifically the DLL hell referenced by https://github.com/dotnet/corefx/issues/25773. The issue is caused by other NuGet packages that contain references to the newer version of System.Net.Http (4.2.0.0). The current solution appears to be to add a binding redirect to downgrade the assembly version to the expected version (4.0.0.0), but so far that has not helped me.
The solution that did work for me was to install the latest NuGet package of System.Net.Http, and use assembly binding redirects in my test project to ensure that it used the 4.2.0.0 version instead of 4.0.0.0.
This is often caused by early versions of nuget packages targeting .NET standard, which have dependencies on OOB ("out-of-band") packages. OOB packages are a kind of polyfill for dlls that are part of .NET framework but not .NET standard. Here is a very good explanation of what happened. In my case, the following helped:
I identified the nuget package that had a dependency on the system.net.http 4.2.0 nuget package, and upgrade that package.
The dependency was no longer present in the upgraded package, so i could uninstall the system.net.http 4.2.0 nuget package.
The upgraded package of course still expects the reference to the system.net.http 4.0.0 assembly, so in case of doubt, you may reinstall the upgraded package to make sure that the assembly reference is in your *.csproj file.
If I download FluentAssertions 1.7.1 (which all of my unit tests are currently written against) then I can do a reference to the dll and the test below will work just fine.
But I tried to upgrade via NuGet and using FluentAssertions versions 3.0.90 and also tried 3.0.107. I tried to make a brand new solution/Class Library project after running into problems, but it can't detect anything from the library...
The test below won't compile because the .Should extension method can't be found for bool... or anything else for that matter. But I can clearly see it exists in the Object Browser I can see FluentAssertions and FluentAssertions.Core in my assembly references, but only view FluentAssertions.Core in the Object Browser. The only thing Intellisence seems to find for extension methods is .ShouldRaisePropertyChangeFor<...> and .ShouldNotRaisePropertyChangeFor<...>.
My project targets .Net4.0 and at this point I'm thinking it's a config problem, but I'm not sure where to start looking if it is. No one else on Google seems to have this issue.
using System;
using FluentAssertions;
using NUnit.Framework;
namespace IntegrationTests.CommonTests
{
[TestFixture]
public class _BasicTemplate_Tests
{
[Test]
public void Run_Basic_Test()
{
true.Should().Be(true);
}
}
}
That's because v3.0 requires 4.0.3 of the .NET framework (which most people are already using).
We're using .NET 4.5 and have had no problem with open generic exports in MEF up until a month ago when it suddenly stopped working. 70 tests on our CI server turned red and we traced it down to missing parts in the container.
I found it strange, so I wrote this test code:
var catalog = new TypeCatalog(typeof(Impersonator<>), typeof(Cache<>));
var parts = catalog.Parts;
But it looks like none of the open generic types will register in the catalog. Parts is a TypeCatalog with Types = <Empty>.
Usually we go about this by declarative exports using attributes as in this SO question, but none of the alternatives seems to be working anymore.
Any idea will be most appreciated.
Any idea what triggered it to start failing? Did you get a .NET update in that timeframe?
Out of curiosity if you write a simple console app targeting .NET 4.5 does this issue repro?
The reason I ask is I believe there was an update released for .NET 4.5 to make sure open generics didn't break existing .NET 4.0 apps and so if for some reason your tests are running in .NET 4.0 mode then then the open generic support will be disabled which could cause what you are seeing.
I resolved this in my web app by putting the entry < httpRuntime targetFramework="4.5" /> in my web.config. For non-web apps, make sure the app.config has the entry
< supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />.
see http://blogs.msdn.com/b/webdev/archive/2012/11/19/all-about-httpruntime-targetframework.aspx for more info.
I know this is an old issue, but I have some additional information that may help others.
I ran into the same issue. My unit tests were working then suddenly stopped. We narrowed it down to a pair of Windows updates: KB2840642v2 and KB2840642. After uninstalling these, MEF started working properly again in unit tests. I didn't see anything in the release notes for those updates that should affect things, but you know how that goes.
However, I am now running Windows 8 unfortunately, and the issue has popped up again. I do not have those updates installed. I think they were incorporated natively into the operating system.
When you say “on our CI server”, that does not tell us much about how you’re running the tests. However, I seem to get an experience like yours using vstest.console.exe.
I found a situation where MEF’s automatic open type closing feature, new in .net-4.5, will appear to break when running tests with vstest.console.exe. Basically, vstest.console.exe might decide to run in .net-4.0 compatibility mode as if /Framework:Framework40 had been passed to it. .net-4.5 assemblies will load and run perfectly fine even when loaded by a .net-4.0 runtime, but MEF detects that you’re running in .net-4.0 mode and disables its support for automatically closing open generic types. You can get MEF to behave properly again by forcing vstest.console.exe to run in .net-4.5 mode by passing the /Framework:Framework45 switch.
(This tested on a machine with .net-4.6.1 installed, though, not sure if that changes things yet more).
Minimal Repro
When I make a simple test assembly targeting the .net-4.5 framework, vstest.console.exe correctly guesses that it should run the tests with .net-4.5. However, when I make an assembly that integrates into a more complex build environment, vstest.console.exe suddenly starts running in .net-4.0 mode rather than .net-4.5 mode. So I started with my complex build environment and started paring things down until the issue disappeared.
To get vstest.console.exe to guess the wrong framework, you need two assemblies. In one, define a custom assembly Attribute. In another, apply that custom assembly attribute and define your unit tests.
First assembly:
using System;
[AttributeUsage(AttributeTargets.Assembly)]
public class BreakVSTestFrameworkDetectionAttribute : Attribute
{
}
Second assembly referencing the prior one:
using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
[assembly: BreakVSTestFrameworkDetection]
[InheritedExport]
public interface IGenericService<T>
{
void Print(T thing);
}
public class SomeGenericService<T> : IGenericService<T>
{
public void Print(T thing) => Console.WriteLine($"{typeof(T)}:{thing}");
}
[TestClass]
public class UnitTest1
{
[TestMethod]
public void Run()
{
using (var catalogue = new ApplicationCatalog())
using (var container = new CompositionContainer(catalogue))
{
container.GetExportedValue<IGenericService<string>>().Print("asdf"); // System.String:asdf
container.GetExportedValue<IGenericService<int>>().Print(123); // System.Int32:123
}
}
static void Main(string[] args) => new UnitTest1().Run();
}
With these two assemblies and the defined test, I get the following outcomes in different scenarios:
Running the tests using Visual Studio 2015 Community Editions’s GUI works perfectly fine.
Running the code directly as an executable works fine.
C:\Users\ohnob\OneDrive\Documents\Visual Studio 2015\Projects\MefGenericsUnitTests>bin\Debug\MefGenericsUnitTests.exe
System.String:asdf
System.Int32:123
Running the tests with something like mstest (except I can’t tell why one would or wouldn’t use this instead of vstest) magically works (at least in this case) (output trimmed).
C:\Users\ohnob\OneDrive\Documents\Visual Studio 2015\Projects\MefGenericsUnitTests>"\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\MSTest.exe" /testcontainer:bin\Debug\MefGenericsUnitTests.exe
Microsoft (R) Test Execution Command Line Tool Version 14.0.23107.0
Copyright (c) Microsoft Corporation. All rights reserved.
Loading bin\Debug\MefGenericsUnitTests.exe...
Starting execution...
Results Top Level Tests
------- ---------------
Passed UnitTest1.Run
1/1 test(s) Passed
Running with straight up vstest.console.exe fails (output trimmed):
C:\Users\ohnob\OneDrive\Documents\Visual Studio 2015\Projects\MefGenericsUnitTests>"\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe" bin\Debug\MefGenericsUnitTests.exe
Microsoft (R) Test Execution Command Line Tool Version 14.0.24720.0
Copyright (c) Microsoft Corporation. All rights reserved.
Starting test execution, please wait...
Failed Run
Error Message:
Test method UnitTest1.Run threw exception:
System.ComponentModel.Composition.ImportCardinalityMismatchException: No exports were found that match the constraint:
ContractName IGenericService(System.String)
RequiredTypeIdentity IGenericService(System.String)
But vstest.console.exe /Framework:Framework45 succeeds:
C:\Users\ohnob\OneDrive\Documents\Visual Studio 2015\Projects\MefGenericsUnitTests>"\Program Files (x86)\Microsoft Visual Studio 14.0\Common7\IDE\CommonExtensions\Microsoft\TestWindow\vstest.console.exe" /Framework:Framework45 bin\Debug\MefGenericsUnitTests.exe
Microsoft (R) Test Execution Command Line Tool Version 14.0.24720.0
Copyright (c) Microsoft Corporation. All rights reserved.
Starting test execution, please wait...
Passed Run
Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.
Test Run Successful.
Test execution time: 3.7855 Seconds
I think you can not use open generic and should provide a concrete running class like:
var catalog = new TypeCatalog(typeof(Impersonator<type1>), typeof(Impersonator<type2>), typeof(Cache<type3>), typeof(Cache<type4>));
var parts = catalog.Parts;
I tried using interfaces like IImpersonator and ICache and does not work also:
interface IImpersonator { }
class Impersonator<T> : IImpersonator
...
var catalog = new TypeCatalog(typeof(IImpersonator), typeof(ICache));
var parts = catalog.Parts;