当前位置: 首页 > 知识库问答 >
问题:

Net核心:在Xunit测试中为AppService、Repository等执行所有依赖项注入

郎慎之
2023-03-14

我试图在AppService的XUnit测试中实现依赖注入。理想的目标是运行原始的应用程序启动/配置,并使用启动中的任何依赖注入,而不是在我的测试中重新初始化所有的DI,这就是整个目标。

更新:莫森的答案很接近。需要更新两个语法/需求错误才能正常工作。

由于某些原因,原始应用程序可以工作,并可以调用部门应用程序服务。然而,它无法调用Xunit。最后,使用原始应用程序的启动和配置使Testserver正常工作。现在接收错误如下:

Message: The following constructor parameters did not have matching fixture data: IDepartmentAppService departmentAppService

namespace Testing.IntegrationTests
{
    public class DepartmentAppServiceTest
    {
        public DBContext context;
        public IDepartmentAppService departmentAppService;

        public DepartmentAppServiceTest(IDepartmentAppService departmentAppService)
        {
            this.departmentAppService = departmentAppService;
        }

        [Fact]
        public async Task Get_DepartmentById_Are_Equal()
        {
            var options = new DbContextOptionsBuilder<SharedServicesContext>()
                .UseInMemoryDatabase(databaseName: "TestDatabase")
                .Options;
            context = new DBContext(options);

            TestServer _server = new TestServer(new WebHostBuilder()
                .UseContentRoot("C:\\OriginalApplication")
                .UseEnvironment("Development")
                .UseConfiguration(new ConfigurationBuilder()
                    .SetBasePath("C:\\OriginalApplication")
                    .AddJsonFile("appsettings.json")
                    .Build()).UseStartup<Startup>());

            context.Department.Add(new Department { DepartmentId = 2, DepartmentCode = "123", DepartmentName = "ABC" });
            context.SaveChanges();

            var departmentDto = await departmentAppService.GetDepartmentById(2);

            Assert.Equal("123", departmentDto.DepartmentCode);
        }
    }
}

我收到这个错误:

Message: The following constructor parameters did not have matching fixture data: IDepartmentAppService departmentAppService

需要在测试中使用依赖注入,就像真正的应用程序一样。最初的应用程序就是这样做的。下面的答案目前还不够,一个答案使用的是非当前目标的嘲弄,另一个答案使用的是绕过问题目的的控制器。

注意:IDepartmentAppService依赖于IDepartmentRepository,后者也被注入到Startup类中,并依赖于Automapper。这就是为什么给整个创业班打电话。

好资源:

如何对asp进行单元测试。net核心应用程序,带有构造函数依赖项注入

XUnit项目中的依赖注入

共有3个答案

尉迟禄
2023-03-14

当你在测试的时候。你需要使用mocking库或者直接在constructor ie上注入你的服务。

public DBContext context;
public IDepartmentAppService departmentAppService;

/// Inject DepartmentAppService here
public DepartmentAppServiceTest(DepartmentAppService departmentAppService)
{
    this.departmentAppService = departmentAppService;
}
秦昂然
2023-03-14

使用自定义Web应用程序工厂和ServiceProvider。GetRequiredService下面,请随意编辑和优化答案

定制Web应用程序工厂:

public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        builder.ConfigureAppConfiguration((hostingContext, configurationBuilder) =>
        {
            var type = typeof(TStartup);
            var path = @"C:\\OriginalApplication";

            configurationBuilder.AddJsonFile($"{path}\\appsettings.json", optional: true, reloadOnChange: true);
            configurationBuilder.AddEnvironmentVariables();
        });

        // if you want to override Physical database with in-memory database
        builder.ConfigureServices(services =>
        {
            var serviceProvider = new ServiceCollection()
                .AddEntityFrameworkInMemoryDatabase()
                .BuildServiceProvider();

            services.AddDbContext<ApplicationDBContext>(options =>
            {
                options.UseInMemoryDatabase("DBInMemoryTest");
                options.UseInternalServiceProvider(serviceProvider);
            });
        });
    }
}

集成测试:

public class DepartmentAppServiceTest : IClassFixture<CustomWebApplicationFactory<OriginalApplication.Startup>>
{
    public CustomWebApplicationFactory<OriginalApplication.Startup> _factory;
    public DepartmentAppServiceTest(CustomWebApplicationFactory<OriginalApplication.Startup> factory)
    {
        _factory = factory;
        _factory.CreateClient();
    }

    [Fact]
    public async Task ValidateDepartmentAppService()
    {      
        using (var scope = _factory.Server.Host.Services.CreateScope())
        {
            var departmentAppService = scope.ServiceProvider.GetRequiredService<IDepartmentAppService>();
            var dbtest = scope.ServiceProvider.GetRequiredService<ApplicationDBContext>();
            dbtest.Department.Add(new Department { DepartmentId = 2, DepartmentCode = "123", DepartmentName = "ABC" });
            dbtest.SaveChanges();
            var departmentDto = await departmentAppService.GetDepartmentById(2);
            Assert.Equal("123", departmentDto.DepartmentCode);
        }
    }
}

资源:

https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2

https://fullstackmark.com/post/20/painless-integration-testing-with-aspnet-core-web-api

西门安宁
2023-03-14

您正在混合单元测试和集成测试TestServer用于集成测试,如果要重用Startup类以避免再次注册依赖项,则应使用HttpClient并对使用IDepartmentAppService的控制器和操作进行HTTP调用。

如果你想进行单元测试,你需要设置DI并注册所有需要的依赖项来测试IDepartmentAppService

通过测试夹具使用DI:

public class DependencySetupFixture
{
    public DependencySetupFixture()
    {
         var serviceCollection = new ServiceCollection();
         serviceCollection.AddDbContext<SharedServicesContext>(options => options.UseInMemoryDatabase(databaseName: "TestDatabase"));
         serviceCollection.AddTransient<IDepartmentRepository, DepartmentRepository>();
         serviceCollection.AddTransient<IDepartmentAppService, DepartmentAppService>();

         ServiceProvider = serviceCollection.BuildServiceProvider();
    }

    public ServiceProvider ServiceProvider { get; private set; }
}

public class DepartmentAppServiceTest : IClassFixture<DependencySetupFixture>
{
    private ServiceProvider _serviceProvide;

    public DepartmentAppServiceTest(DependencySetupFixture fixture)
    {
        _serviceProvide = fixture.ServiceProvider;
    }

    [Fact]
    public async Task Get_DepartmentById_Are_Equal()
    {
        using(var scope = _serviceProvider.CreateScope())
        {   
            // Arrange
            var context = scope.ServiceProvider.GetServices<SharedServicesContext>();
            context.Department.Add(new Department { DepartmentId = 2, DepartmentCode = "123", DepartmentName = "ABC" });
            context.SaveChanges();

            var departmentAppService = scope.ServiceProvider.GetServices<IDepartmentAppService>();

            // Act
            var departmentDto = await departmentAppService.GetDepartmentById(2);

            // Arrange
            Assert.Equal("123", departmentDto.DepartmentCode);           
        }
    }
}

在单元测试中使用依赖注入不是一个好主意,你应该避免这种情况。顺便说一句,如果你不想重复你自己注册依赖项,你可以把你的DI配置包装在另一个类中,并在任何你想要的地方使用那个类。

通过启动使用DI。反恐精英:

public class IocConfig
{
    public static IServiceCollection Configure(IServiceCollection services, IConfiguration configuration)
    {
         serviceCollection
            .AddDbContext<SomeContext>(options => options.UseSqlServer(configuration["ConnectionString"]));
         serviceCollection.AddScoped<IDepartmentRepository, DepartmentRepository>();
         serviceCollection.AddScoped<IDepartmentAppService, DepartmentAppService>();
         .
         .
         .

         return services;
    }
}

Startup类和ConfigureServices方法中,只需使用IocConfig类:

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    public void ConfigureServices(IServiceCollection services)
    {
         IocConfig.Configure(services, configuration);

         services.AddMvc();
         .
         .
         .

如果您不想使用IocConfig类,请更改Startup类中的配置服务

public IServiceCollection ConfigureServices(IServiceCollection services)
{
     .
     .
     .
     return services;

在测试项目中,重用IocConfigStartup类:

public class DependencySetupFixture
{
    public DependencySetupFixture()
    {
          var builder = new ConfigurationBuilder()
                .SetBasePath(Directory.GetCurrentDirectory())
                .AddJsonFile("appsettings.json", false, true));
         configuration = builder.Build();

         var services = new ServiceCollection();

         // services = IocConfig.Configure(services, configuration)
         // or
         // services = new Startup(configuration).ConfigureServices(services);

         ServiceProvider = services.BuildServiceProvider();
    }

    public ServiceProvider ServiceProvider { get; private set; }
}

并在试验方法上:

[Fact]
public async Task Get_DepartmentById_Are_Equal()
{
    using (var scope = _serviceProvider.CreateScope())
    {
        // Arrange
        var departmentAppService = scope.ServiceProvider.GetServices<IDepartmentAppService>();

        // Act
        var departmentDto = await departmentAppService.GetDepartmentById(2);

        // Arrange
        Assert.Equal("123", departmentDto.DepartmentCode);
    }
}
 类似资料:
  • 如何在.NET核心库项目中将一个类注入另一个类?在API项目中的StartUp类ConfigureServices中,我应该在哪里配置DI?

  • 我有一个ASP。NET核心2.1站点,注册了几个DbContext和一些条件连接字符串。 出于调试目的,我想列出所有注册的<code>DbContext</code>及其连接字符串,以确保所有内容都配置良好。 这只是一个“测试”功能,不用于任何其他用途,因此请不要回答“您不应该这样做”、“它是反模式”等。

  • 复合在这种情况下“消耗”一个子对象数组,从某种意义上说,“吸入”用密钥注册的IFoo的每个实现。这是一个重要的方面:如果您要用一个键注册复合,它将尝试实例化自己,并立即导致StackOverflow异常。 本机DI不支持这些命名注册。 这个例子是从这里提取的

  • 我的大多数服务通常与crud方法相同

  • 我已经为主项目和测试创建了.NET Standard1.6类库,并复制了代码。我将单元测试改为使用xUnit属性和断言,而不是NUnit。 除此之外,我基本上遵循了文档中的说明,因此添加了以下NuGet包: microsoft.net.test.sdk xUnit xunit.runner.visualstudio

  • 我有一个.NET core项目,并且正在尝试使用授权选项创建自定义策略,如此处的文档所示: ASP.NET.Core授权-需求处理程序中的依赖注入 这些示例显示了使用 1 个参数(一个简单的 int 值)设置授权要求。我的自定义要求需要一个字符串参数以及一个 DbContext 对象。我想在运行时将 DbContext 注入到需求的构造函数中。我正在使用 Autofac 容器。我不确定如何实现这一