Reputation: 5596
I took a legacy .NET framework application, that consisted of class libraries and a unit test project only, and converted them all to .NET core 3.1.
I can't seem to execute or bootstrap the unit tests with the .NET core method of using the IHostBuilder
's CreateHostBuilder
.
When I debug any of my unit tests, it stops executing and I get this error:
The following constructor parameters did not have matching fixture data: IOptions`1 appConfig, IServiceX serviceX, ITemplateEngine templateEngine Exception doesn't have a stacktrace
Sample unit test:
public class TemplateGenerationTests : BaseTest
{
private readonly AppConfiguration appConfig;
private readonly IServiceX serviceX;
private readonly ITemplateEngine templateEngine;
public TemplateGenerationTests(IOptions<AppConfiguration> appConfig, IServiceX serviceX, ITemplateEngine templateEngine)
{
this.appConfig = appConfig.Value;
this.serviceX = serviceX;
this.templateEngine = templateEngine;
}
[Fact]
public void SomeTestIhave()
{
//removed for brevity
serviceX.DoWork(appConfig.SomeAppProp);
Assert.NotNull(result);
Assert.NotEmpty(result);
}
}
Base class (I have a feeling this is the incorrect way of creating the hostbuiler):
public class BaseTest
{
public BaseTest()
{
var builder = CreateHostBuilder();
Task.Run(() => builder.RunConsoleAsync());
}
public static IHostBuilder CreateHostBuilder(string[] args = null) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddJsonFile("appsettings.json", optional: true);
config.AddEnvironmentVariables();
if (args != null)
{
config.AddCommandLine(args);
}
})
.ConfigureServices((hostContext, services) =>
{
services.AddOptions();
services.Configure<AppConfiguration>(hostContext.Configuration.GetSection("AppConfiguration"));
services.AddTransient<IServiceX, ServiceX>();
services.AddTransient<ITemplateEngine, TemplateEngine>();
//removed for brevity..
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
});
}
Ideally, I am looking for a single point where I can setup the HostBuilder
i.e. in a console or API .NET project, this would be in the Main
method of the Program
class.
Upvotes: 1
Views: 6725
Reputation: 581
No need to copy the CreateHostBuilder
method from Program.cs
(DRY). Just set its visibility to public
(or internal
using [assembly: InternalsVisibleToAttribute("Your.Test.Project")]
) and create a little Helper
class in Your.Test.Project
:
// using Microsoft.Extensions.DependencyInjection;
// using Microsoft.Extensions.Hosting;
public static T GetRequiredService<T>()
{
IHost testHost = Program.CreateHostBuilder().Build();
return testHost.Services.GetRequiredService<T>();
}
Your test class:
public class TemplateGenerationTests
{
private IServiceX serviceX;
private AppConfiguration appConfig;
public TemplateGenerationTests()
{
serviceX = Helper.GetRequiredService<IServiceX>();
appConfig = Helper.GetRequiredService<AppConfiguration>();
}
[Fact]
public void SomeTestIhave()
{
// Act
var result = serviceX.DoWork(appConfig.SomeAppProp);
// Assert
Assert.NotNull(serviceX);
Assert.NotEmpty(result);
}
}
Upvotes: 0
Reputation: 11544
With the small modification you can get rid of injecting services into TemplateGenerationTests
class:
The BaseTest
class:
public class BaseTest
{
public BaseTest()
{
TestHost = CreateHostBuilder().Build();
Task.Run(() => TestHost.RunAsync());
}
public IHost TestHost { get; }
public static IHostBuilder CreateHostBuilder(string[] args = null) =>
Host.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((hostingContext, config) =>
{
config.AddJsonFile("appsettings.json", optional: true);
config.AddEnvironmentVariables();
if (args != null)
{
config.AddCommandLine(args);
}
})
.ConfigureServices((hostContext, services) =>
{
services.AddOptions();
services.Configure<AppConfiguration>(hostContext.Configuration.GetSection("AppConfiguration"));
services.AddTransient<IServiceX, ServiceX>();
services.AddTransient<ITemplateEngine, TemplateEngine>();
//removed for brevity..
})
.ConfigureLogging((hostingContext, logging) =>
{
logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging"));
logging.AddConsole();
});
}
And the TemplateGenerationTests
class:
public class TemplateGenerationTests : IClassFixture<BaseTest>
{
private readonly IHost _host;
public TemplateGenerationTests(BaseTest baseTest)
{
_host = baseTest.TestHost;
}
[Fact]
public void SomeTestIhave()
{
// Arrange
var serviceX = _host.Services.GetService<IServiceX>();
var appConfig = _host.Services.GetService<AppConfiguration>();
// Act
var result = serviceX.DoWork(appConfig.SomeAppProp);
// Assert
Assert.NotNull(serviceX);
Assert.NotEmpty(result);
}
}
Upvotes: 7