如何解决如何使用 xUnit 为使用 Entity Framework Core 和 Simple Injector 构建的 Asp.NetCore WebAPI 构建测试?
我使用 Entity Framework Core 和 Simple Injector 创建了一个 ASP.NET Core Web API。
我想使用 xUnit 进行单元测试来测试我的控制器。
我不知道从哪里开始。我相信我必须在我的单元测试中模拟一个容器对象。
这是容器初始化的启动代码:
public class Startup
{
private Container container;
public IConfiguration Configuration { get; }
private IConfigurationRoot configurationRoot;
public Startup(IConfiguration configuration)
{
Configuration = configuration;
// Build configuration info
configurationRoot = new ConfigurationBuilder()
.AddJsonFile("appsettings.json",optional: true,reloadOnChange: true)
.Build();
}
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
InitializeContainer();
services.AddSimpleInjector(container,options =>
{
options.AddAspNetCore()
.AddControllerActivation();
options.AddLogging();
});
}
private void InitializeContainer()
{
container = new SimpleInjector.Container();
container.Options.ResolveUnregisteredConcreteTypes = false;
container.ConfigureServices();
}
public void Configure(IApplicationBuilder app,IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthorization();
app.UseSimpleInjector(container);
AppSettingsHelper.AppConfig = configurationRoot;
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
这是我的服务安装程序的代码:
public static class ServicesInstaller
{
public static void ConfigureServices(this Container container)
{
container.Options.DefaultScopedLifestyle = new AsyncScopedLifestyle();
//Assembly.Load will not re-load already loaded Assemblies
container.Register<IFooContext,FooContext>(Lifestyle.Scoped);
container.Register<FooContext>(Lifestyle.Scoped);
}
}
这是一个示例控制器:
[Route("[controller]")]
[ApiController]
public class SomeController : ControllerBase
{
private readonly ILogger<SomeController> _logger;
private readonly Container _container;
public SomeController(ILogger<SomeController> p_Logger,Container p_Container)
{
_logger = p_Logger;
_container = p_Container;
}
[HttpGet]
[Route("{p_SomeId}")]
public Some GetownerByOwnerId(Guid p_SomeId)
{
Some some;
using (Scope scope = AsyncScopedLifestyle.BeginScope(_container))
{
var dbContext = _container.GetInstance<FooContext>();
some = dbContext.somes.Where(x => x.someId == p_SomeId).FirstOrDefault();
}
return some;
}
}
我对使用 SimpleInjector 比较陌生。
我将如何模拟用于测试的容器?
解决方法
所提供示例中的控制器不应耦合到任何特定于容器的内容。
显式地将必要的依赖项注入控制器。
[Route("[controller]")]
[ApiController]
public class SomeController : ControllerBase {
private readonly ILogger<SomeController> _logger;
private readonly IFooContext dbContext;
public SomeController(ILogger<SomeController> p_Logger,IFooContext dbContext) {
_logger = p_Logger;
this.dbContext = dbContext;
}
[HttpGet]
[Route("{p_SomeId}")]
public Some GetOwnerByOwnerId(Guid p_SomeId) {
Some some = dbContext.Somes.Where(x => x.SomeId == p_SomeId).FirstOrDefault();
return some;
}
}
现在不需要模拟容器,这会被视为实现细节代码异味。
在执行单元测试时模拟依赖项抽象并验证预期行为。
控制器也应该尽可能精简,因为大多数其他横切关注点,比如确保注入的上下文是有范围的,是由框架在启动时通过配置的容器处理的。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。