.net core 3.0, issue with IClassFixture, "unresolved constructor arguments: ITestOutputHelper output"
Asked Answered
K

2

5

I made a repo for the issue I have with Integration testing upgrading to .net core 3.0 : https://github.com/ranouf/TestingWithDotNetCore3_0

When I launch the test, I have this issue: Message:

System.AggregateException : One or more errors occurred. (Class fixture type 'MyIntegrationTests.TestServerFixture' had one or more unresolved constructor arguments: ITestOutputHelper output) (The following constructor parameters did not have matching fixture data: TestServerFixture testServerFixture) ---- Class fixture type 'MyIntegrationTests.TestServerFixture' had one or more unresolved constructor arguments: ITestOutputHelper output ---- The following constructor parameters did not have matching fixture data: TestServerFixture testServerFixture Stack Trace: ----- Inner Stack Trace #1 (Xunit.Sdk.TestClassException) ----- ----- Inner Stack Trace #2 (Xunit.Sdk.TestClassException) -----

Here is the constructor:

public class WeatherForecastController_Tests : IClassFixture<TestServerFixture>
{
    public WeatherForecastController_Tests(TestServerFixture testServerFixture, ITestOutputHelper output)
    {
        Client = testServerFixture.Client;
        Output = output;
    }

TestStartup:

public class TestStartup : Startup
{
    public TestStartup(IConfiguration configuration)
        : base(configuration)
    {

    }

    public override void SetUpDataBase(IServiceCollection services)
    {
        // here is where I use the InMemoryDatabase 
    }
}

TestServerFixture:

public class TestServerFixture : WebApplicationFactory<TestStartup>
{
    private IHost _host;
    public HttpClient Client { get; }
    public ITestOutputHelper Output { get; }

    public TestServerFixture(ITestOutputHelper output)
    {
        Output = output;
        Client = Server.CreateClient();
    }

    // never called but this is where i was previously building up the server
    //
    protected override TestServer CreateServer(IWebHostBuilder builder)
    {
        return base.CreateServer(builder);
    }

    protected override IHost CreateHost(IHostBuilder builder)
    {
        _host = builder.Build();

        using (var scope = _host.Services.CreateScope())
        {
            var services = scope.ServiceProvider;
            InitializeDataBase(services, Output);
        }

        _host.Start();
        return _host;
    }

    protected override IHostBuilder CreateHostBuilder() =>
        Host.CreateDefaultBuilder()
            .ConfigureLogging((hostingContext, builder) =>
            {
                builder.Services.AddSingleton<ILoggerProvider>(new XunitLoggerProvider(Output));
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder.UseTestServer();
            });

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.UseStartup<TestStartup>();
    }

    private void InitializeDataBase(IServiceProvider services, ITestOutputHelper output)
    {
        try
        {
            output.WriteLine("Starting the database initialization.");
            //here is where is feed the Test DB
            output.WriteLine("The database initialization has been done.");
        }
        catch (Exception ex)
        {
            output.WriteLine("An error occurred while initialization the database.");
            Console.WriteLine(ex.Message);
        }
    }
}

So clearly, the Iod for TestServerFixture testServerFixture and ITestOutputHelper output doesnt work. How to make it work?

Kirkcudbright answered 3/12, 2019 at 23:14 Comment(1)
I was able to do it with .net core 2.0 and without WebApplicationFactory and IClassFixture. Do you know maybe another way to do it in .net core 3.0? It s interesting to be able to have some details when a test failed.Kirkcudbright
K
9

Thanks @Nkosi for your help, It helps me to find a solution :). I was inspired by other websites too:

I updated the solution on my repo too

Here are important parts:

TestServerFixture:

public class TestServerFixture : WebApplicationFactory<TestStartup>
{
    public HttpClient Client { get; }
    public ITestOutputHelper Output { get; set; }

    protected override IHostBuilder CreateHostBuilder()
    {
        var builder = Host.CreateDefaultBuilder()
            .ConfigureLogging(logging =>
            {
                logging.ClearProviders(); //All API logging providers are cleared
                logging.AddXunit(Output); //Internal extension which redirect all log to the ITestOutputHelper 
            })
            .ConfigureWebHostDefaults(webBuilder =>
            {
                webBuilder
                    .UseStartup<TestStartup>()
                    .ConfigureTestServices((services) =>
                    {
                        //Without that, the client always returns 404
                        //(even if it has already been set in Startup.Configure)
                        services
                            .AddControllers()
                            .AddApplicationPart(typeof(Startup).Assembly);
                    });
            });

        return builder;
    }

    //ITestOutputHelper is set in the constructor of the Test class
    public TestServerFixture SetOutPut(ITestOutputHelper output)
    {
        Output = output;
        return this;
    }

    protected override void Dispose(bool disposing)
    {
        base.Dispose(disposing);
        Output = null;
    }
}

WeatherForecastController_Tests:

public class WeatherForecastController_Tests : IClassFixture<TestServerFixture>
{
    public TestServerFixture TestServerFixture { get; private set; }
    public HttpClient Client { get; private set; }
    public ITestOutputHelper Output { get { return TestServerFixture.Output; } }

    public WeatherForecastController_Tests(TestServerFixture testServerFixture, ITestOutputHelper output)
    {
        TestServerFixture = testServerFixture.SetOutPut(output);
        Client = testServerFixture.CreateClient();
    }

    [...]
}

Let me know if you have any suggestions to improve the code or questions :)

Kirkcudbright answered 7/12, 2019 at 2:19 Comment(1)
Well... what do you know..., this works... thanks for sharing, I found this from a GitHub link someone shared.Managua
G
2

I reviewed your code and after some research I have come to the conclusion that injecting ITestOutputHelper into TestServerFixture via constructor injection is not feasible. I looked into property injection as well but I believe the it may end up getting used before the property has been populated.

The main issue here is the flow of how things get invoked when the WebApplicationFactory is created.

By creating the Client in the constructor, it triggers a sequence of events that prevent you from being able to make use of the ITestOutputHelper

I suggest deferring the creation of the Client so that dependencies can be set before.

public class TestServerFixture : WebApplicationFactory<TestStartup> {
    private Lazy<HttpClient> client = new Lazy<HttpClient>(() => return Server.CreateClient());

    private IHost _host;
    public HttpClient Client => client.Value;
    public ITestOutputHelper Output { get; set; }

    public TestServerFixture(){
        //...
    }

    //...all other code remains the same
}

Note the Lazy<HttpClient>. This is to defer the creation of the client so that the ITestOutputHelper can be populate first.

public class WeatherForecastController_Tests : IClassFixture<TestServerFixture> {
    public WeatherForecastController_Tests(TestServerFixture testServerFixture, ITestOutputHelper output) {
        Output = output;
        testServerFixture.Output = Output;
        Client = testServerFixture.Client;            
    }

    //...
Greasewood answered 4/12, 2019 at 23:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.