dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.23k stars 9.95k forks source link

ConfigureTestServices : since 3.0 does not override my service configuration for addhttpClient #17937

Open SebHonorine opened 4 years ago

SebHonorine commented 4 years ago

Describe the bug

I have an app that was in ASP .Net Core 2.1 and updated it to 3.1 recently. I have some integration tests in my solution. I used the Microsfot doc when created it : https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1

When I updated everything and tried to keep using my "ConfigureTestServices", now I have an error like "The HttpClient factory already has a registered client with the type '#######.####'. Client types must be unique. Consider using inheritance to create multiple unique types with the same API surface."

From what I've read seems to be related to changes to IHostBuilder ?

To Reproduce

Code could be found here : https://github.com/SebHonorine/BugTest

2 integration tests are in error : Submit_Should_Delete_Cart_After_Post Delete_Should_Delete_Cart

Just launch the test that use ConfigureTestServices to ovveride my "AddHttpClient"

Message : -->System.InvalidOperationException : The HttpClient factory already has a registered client with the type 'AccesTi.Carts.Infrastructure.IDispatcherClient'. Client types must be unique. Consider using inheritance to create multiple unique types with the same API surface.

Further technical details

.NET Core SDK (reflecting any global.json): Version: 3.1.100 Commit: cd82f021f4

Runtime Environment: OS Name: Windows OS Version: 10.0.14393 OS Platform: Windows RID: win10-x64 Base Path: C:\Program Files\dotnet\sdk\3.1.100\

Host (useful for support): Version: 3.1.0 Commit: 65f04fb6db

.NET Core SDKs installed: 2.1.403 [C:\Program Files\dotnet\sdk] 2.1.600-preview-009426 [C:\Program Files\dotnet\sdk] 2.1.600 [C:\Program Files\dotnet\sdk] 2.1.602 [C:\Program Files\dotnet\sdk] 2.1.700 [C:\Program Files\dotnet\sdk] 2.1.701 [C:\Program Files\dotnet\sdk] 2.1.801 [C:\Program Files\dotnet\sdk] 2.1.802 [C:\Program Files\dotnet\sdk] 3.0.100-preview8-013656 [C:\Program Files\dotnet\sdk] 3.1.100 [C:\Program Files\dotnet\sdk]

.NET Core runtimes installed: Microsoft.AspNetCore.All 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.All 2.1.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.All] Microsoft.AspNetCore.App 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.6 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 2.1.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.0.0-preview8.19405.7 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.AspNetCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App] Microsoft.NETCore.App 2.1.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.8 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.9 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.11 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.12 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.13 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 2.1.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.0.0-preview8-28405-07 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App] Microsoft.WindowsDesktop.App 3.0.0-preview8-28405-07 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App] Microsoft.WindowsDesktop.App 3.1.0 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]

I did this post last week : https://github.com/aspnet/AspNetCore.Docs/issues/16100 might reference it.

Thank you and sorry for bothering you guys.

mkArtakMSFT commented 4 years ago

Thanks for contacting us. We will look into this during the 5.0 release.

mfjerome commented 4 years ago

👍 +1 for this issue.

After upgrading our project to .NET Core 3.1, a big part of our integration test suite is failing because our mocked http clients are no longer being registered in the tests to override the real implementations from the startup.

There is no plan to release bug fixes to .NET Core 3.x before .NET 5 next year? It feels a bit far..

johnkors commented 4 years ago

I had a similar experience, where I ended up manually adjusting my container in tests:

var serviceDescriptors = services.Where(descriptor => descriptor.ServiceType == typeof(IDoStuff));
foreach (var service in serviceDescriptors)
{
    var t = services.Remove(service);
}

services.AddSingleton<IDoStuff, Mock>(s => new Mock());

But adding to the discussion:

Client types must be unique.

In .NET Core 2.1, we could create many HttpClients from the same type (class) using named types. I used named types for configuring the same client with different configuration, for example as in here. Is that no longer supported in .NET Core 3.1?

SebHonorine commented 4 years ago

I tried to do the remove trick and it does not work for httpClient. Also, my issue here is that we are loosing the main feature of overriding services as it used to in integration test. We loosing an awesome feature in my opinion and waiting .net 5 is kinda hard to accept. But yeah i should have been more carefull during 3.0 preview.

johnkors commented 4 years ago

Yeah, the manual removal trick I mentioned was not for HttpClientFactory based HttpClient registrations, as that is a bit more involved.

It seems like there is a breaking change in .NET Core 3 when it comes to registering multiple HttpClients of the same type, meaning you would have to migrate to using named clients instead.

In .NET Core 2.1, this was supported:

services.AddHttpClient<MyHttpClient>("client1");
services.AddHttpClient<MyHttpClient>("client2");
services.AddHttpClient<MyHttpClient>("client3");

var httpClient = _clientFactory.CreateClient("client1");

In .NET Core >3.0, the above throws exceptions at startup (The HttpClient factory already has a registered client with the type, and we have to either subclass MyHttpClient, or migrate to non-typed clients using named registrations instead:

services.AddHttpClient("client1");
services.AddHttpClient("client2");
services.AddHttpClient("client3");

var httpClient = _clientFactory.CreateClient("client1");

It's not mentioned in the documentation for breaking changes, @mkArtakMSFT ..?

https://docs.microsoft.com/en-us/dotnet/core/compatibility/2.2-3.0

johnkors commented 4 years ago

I found this issue about the regression, https://github.com/aspnet/Extensions/pull/2710

So it looks like there will be a fix in 3.1.2.

SebHonorine commented 4 years ago

ho good findings. Seems to be related. I though the ConfigureTestService was doing a real override of the httpClient of the startup. It looks like it just add the httpClient and doing a kind of validation to change it to the fake one during runtime. But thank you.

KristofferBerge commented 4 years ago

@mkArtakMSFT Is this still planned for the 5.0.0 release?

ghost commented 3 years ago

We've moved this issue to the Backlog milestone. This means that it is not going to be worked on for the coming release. We will reassess the backlog following the current release and consider this item at that time. To learn more about our issue management process and to have better expectation regarding different types of issues you can read our Triage Process.