Skip to content

WebApplicationFactory does not work from when CreateHostBuilder is from another class + assembly, instead of the entry point assembly. #17994

@PureKrome

Description

@PureKrome

Related

This issue is related to #8210 and (archived) Hosting #1517


Problem

The WebApplicationFactory doesn't work right when the "entry point assembly" references another assembly which contains the CreateHostBuilder logic. Current code (I think) requires a method called CreateHostBuilder to exist in the entry point assembly, only.


Repo

A full repo of this problem can be found here in GitHub.


Description

I have a pretty standard ASP.NET Core 3.1 web api application and i'm trying to use the WebAppFactory to test my web application.

For my program.cs file, instead of adding my custom code in there (e.g. wire up Serilog, etc), I actually have a nuget package which I then re-use everywhere. So if I change my program.cs logic, I can just update various webapi applications with the latest nuget, instead of having to change the copy/paste code in each webapi program.cs class. Works really well.

Given this type of setup, the WebApplicationFactory fails to ResolveHostBuilderFactory because the entry point assembly doesn't contain the CreateHostBuilder<T> method. And therefore stuff isn't working 100% right.

For example.

this is my program.cs

public class Program
{
    public static Task Main()
    {
        var options = new MainOptions
        {
            FirstLoggingInformationMessage = "~~~ Starting FooBar Web Api  ~~~"
        };

        return Homely.AspNetCore.Hosting.CoreApp.Program.Main<Startup>(options);
    }
}

That's it. Notice how it's very different to the default template on first glance. So my program.cs class literally says: -> call some other assembly's Main method and do stuff. It's this other Main<T>(..) method that contains the typical template logic.

(That Main method logic is literally -> setup Serilog, do the normal program.cs templated stuff ... then flush the Serilog on exit. So just logging plumbing wrapped around the default program.cs Main logic).

Okay - so my entry point assembly just calls some other assembly. Nothing crazy.

Except ... it doesn't work in this case because the WebApplicationFactory.CreateHostBuilder() code does this....

protected virtual IHostBuilder CreateHostBuilder()
{
    var hostBuilder = HostFactoryResolver.ResolveHostBuilderFactory<IHostBuilder>(typeof(TEntryPoint).Assembly)?.Invoke(Array.Empty<string>());
    
<snip>
}

and that RHBF method then does this ...

        public static Func<string[], THostBuilder> ResolveHostBuilderFactory<THostBuilder>(Assembly assembly)
        {
            return ResolveFactory<THostBuilder>(assembly, CreateHostBuilder);
        }

        private static Func<string[], T> ResolveFactory<T>(Assembly assembly, string name)
        {
            var programType = assembly?.EntryPoint?.DeclaringType;
            if (programType == null)
            {
                return null;
            }

           <snip>
        }

and the assembly?.EntryPoint?.DeclaringType ends up returning null. Sadness :(

So .. I'm not sure what can be done here, for this scenario.
It's like I wish the code could 'see' that the entry point assembly is not the right place to check, but another assembly which I tell it, is what should be checked.

Secondly, I also tried this:

public class WebAppFactory : WebApplicationFactory<Homely.AspNetCore.Hosting.CoreApp.Program>

but that errored with: System.InvalidOperationException : The provided Type 'Program' does not belong to an assembly with an entry point. A common cause for this error is providing a Type from a class library. That's exactly what I'm trying to do, though :P

So - would love some help, please?


$ dotnet --info
.NET Core SDK (reflecting any global.json):
 Version:   3.1.100
 Commit:    cd82f021f4

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.18362
 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

Metadata

Metadata

Assignees

Labels

affected-fewThis issue impacts only small number of customersarea-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcarea-mvcIncludes: MVC, Actions and Controllers, Localization, CORS, most templatesenhancementThis issue represents an ask for new feature or an enhancement to an existing onefeature-mvc-testingMVC testing packageinvestigateseverity-majorThis label is used by an internal tool

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions