aspnetcore: Integration tests are not working if using inherited Startup and reference to Microsoft.AspNetCore.App without version specified
Describe the bug
We use integration tests in our AspNetCore application. We use inherited Startup class in our test project, so we can easily modify things in it for the testing purposes. The SUT is web API project that references Microsoft.AspNetCore.App. Everything works, if this reference has version specified. If we remove the version specification, the tests stop working. Requests to the test server keep returning 404 NotFound.
Version specification of this package is not recommended. If it is there, the compiler generates warning:
Warning NETSDK1071 A PackageReference to ‘Microsoft.AspNetCore.App’ specified a Version of 2.2.0. Specifying the version of this package is not recommended. For more information, see https://aka.ms/sdkimplicitrefs
Integration tests are always working (regardless of reference version specification) if they directly use Startup class from SUT.
To Reproduce
- Create a simple web API project. Make sure, the reference to
Microsoft.AspNetCore.Appin.csprojis without version specified:<PackageReference Include="Microsoft.AspNetCore.App" /> - Create integration tests project, that references web API project.
- Create a
Startupclass in test project, which is inherited form API’s projectStartup. - Make a simple test which uses test
Startupand makes a request to correct API in test server. - The request should return
200 OK, but returns404 NotFound. - Modify the refeference in API project to have version specified:
<PackageReference Include="Microsoft.AspNetCore.App" Version="2.2.0" />The test will work.
Expected behavior
Integration tests should work as expected even if the package is without version.
Additional context
I created a full (yet simple) demo repository so you can clone it, run the tests and see the results.
There are two web API projects, which are the same. The only difference is how the Microsoft.AspNetCore.App package is referenced. And there are 4 sets of 2 test projects, where one project references API with version, the other test project API without version. 4 sets are because I used different approaches:
- Direct using of
Startupclass in API project. In this case, always everything works. - Using inherited
Startupclass. Non of these approaches works with API project without version.- Using this inherited class directly.
- Using inherited
WebApplicationFactory. - Using
TestServer.
About this issue
- Original URL
- State: closed
- Created 5 years ago
- Comments: 18 (17 by maintainers)
I can confirm what @satano has reported - MVC is not discovering the controllers as expected. The source of the problem is one we’ve seen in a variety of ways before, and as @javiercn guessed, it is a problem with interpreting dependency context.
Context: when user code calls
.AddMvc()insideStartup.ConfigureServices, MVC attempts to answer the question, “which assemblies contain controllers?” It does this through a variety of methods, one of which includes reading the .deps.json file to determine if the assembly references Microsoft.AspNetCore.Mvc. This eventually calls into this code: https://github.com/aspnet/AspNetCore/blob/release/2.2/src/Mvc/Mvc.Core/src/ApplicationParts/ApplicationAssembliesProvider.csWhat’s broken: Because of the way the .NET Core SDK implemented versionless PackageReference, the .deps.json file is generated with the dependency information omitted. In @satano’s repro, this is visible if you compare
InheritedStartupTests/bin/Debug/netcoreapp2.2/InheritedStartupTests.deps.jsonandInheritedStartupTests_WithVersion/bin/Debug/netcoreapp2.2/InheritedStartupTests_WithVersion.deps.json. On the left, the .deps.json generated when using the SDK as directed. On the right, the result of adding theVersionattribute.In the scenario on the left (the versionless package ref), MVC skips controller discover on the app’s entry assembly because it does not think MVC is used.
One potential workaround: You could try manually telling MVC which assemblies have controllers by using
ConfigureApplicationPartManagerlike this.cc @javiercn @pranavkm
We have some plans to address this in 3.0. Assigning this to myself, and I can post an update once we’ve vetted the design.
The difference is that in ProjectStartupTests you are using
WebApplicationFactory<DemoApi.Startup>. This makesDemoApi.dllthe “entry assembly” because this is the assembly containing the startup type, and MVC always searches the entry assembly for controllers. On the other hand, the MVC entry assembly in InheritedStartupTests is the test assembly itself,InheritedStartupTests.dll, because you are usingWebApplicationFactory<InheritedStartupTests.TestsStartup>