Asp.net core 如何使用xUnit为使用实体框架核心和简单注入器构建的Asp.NetCore WebAPI构建测试?

Asp.net core 如何使用xUnit为使用实体框架核心和简单注入器构建的Asp.NetCore WebAPI构建测试?,asp.net-core,dependency-injection,entity-framework-core,xunit,simple-injector,Asp.net Core,Dependency Injection,Entity Framework Core,Xunit,Simple Injector,我使用实体框架核心和简单注入器创建了一个ASP.NET核心Web API 我希望使用xUnit进行单元测试来测试我的控制器 我不知道从哪里开始。我相信我必须在单元测试中模拟容器对象 以下是初始化容器的启动代码: public class Startup { private Container container; public IConfiguration Configuration { get; } private IConfigurationRoot configur

我使用实体框架核心和简单注入器创建了一个ASP.NET核心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);
    }
}
公共静态类服务安装程序
{
公共静态void配置服务(此容器)
{
container.Options.DefaultScopedLifestyle=新的AsyncScopedLifestyle();
//程序集。加载将不会重新加载已加载的程序集
容器。寄存器(生活方式。范围);
容器。寄存器(生活方式。范围);
}
}
以下是一个示例控制器:

[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;
    }
}
[路由(“[控制器]”)]
[ApiController]
公共类控制器:ControllerBase
{
专用只读ILogger\u记录器;
私有只读容器_容器;
公共控制器(ILogger p_记录器、容器p_容器)
{
_记录器=p_记录器;
_容器=p_容器;
}
[HttpGet]
[路线(“{p_SomeId}”)]
公共某些GetOwnerByOwnerId(Guid p_SomeId)
{
有的有的,;
使用(Scope Scope=AsyncScopedLifestyle.BeginScope(_容器))
{
var dbContext=_container.GetInstance();
some=dbContext.Somes.Where(x=>x.SomeId==p_SomeId).FirstOrDefault();
}
还一些;
}
}
我对使用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;
    }
}
[路由(“[控制器]”)]
[ApiController]
公共类控制器:ControllerBase{
专用只读ILogger\u记录器;
私有只读IFooContext-dbContext;
公共SomeController(ILogger p_记录器,IFooContext dbContext){
_记录器=p_记录器;
this.dbContext=dbContext;
}
[HttpGet]
[路线(“{p_SomeId}”)]
公共某些GetOwnerByOwnerId(Guid p_SomeId){
Some Some=dbContext.Somes.Where(x=>x.SomeId==p_SomeId).FirstOrDefault();
还一些;
}
}
现在不需要模拟容器,这将被视为实现细节代码气味

模拟依赖项抽象,并在执行单元测试时验证预期行为

控制器还应尽可能精简,因为大多数其他横切关注点(如确保注入的上下文的作用域)在启动时由框架通过配置的容器进行处理