将SeedData注入自定义WebApplicationFactory的最佳方法

时间:2019-01-15 15:29:13

标签: c# asp.net-core entity-framework-core

我在整合测试中the毁了mayysoft docs网站上的以下代码,并对其进行了一些改动以满足我的需求:

public class CustomWebApplicationFactory<TStartup>
    : WebApplicationFactory<TStartup> where TStartup : class
{
    private readonly SeedDataClass _seed;

    public CustomWebApplicationFactory(SeedDataClass seed)
    {
        _seed = seed;
    }

    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        base.ConfigureWebHost(builder);
        builder.UseEnvironment("Development");
        builder.ConfigureServices(services =>
        {
            var serviceProvider = new ServiceCollection()
                .AddEntityFrameworkInMemoryDatabase()
                .BuildServiceProvider();

            services.AddSingleton(_seed);

            services.AddDbContextPool<GatewayContext>(options =>
            {
                options.UseInMemoryDatabase("InMemoryDbForTesting");
                options.UseInternalServiceProvider(serviceProvider);
                options.EnableSensitiveDataLogging();
            });

            var sp = services.BuildServiceProvider();

            using (var scope = sp.CreateScope())
            {
                var scopedServices = scope.ServiceProvider;
                var db = scopedServices.GetRequiredService<GatewayContext>();
                var logger = scopedServices
                    .GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();

                var seed = scopedServices.GetRequiredService<SeedDataClass>();

                db.Database.EnsureCreated();

                try
                {
                    seed.InitializeDbForTests(db);
                }
                catch (Exception ex)
                {
                    logger.LogError(ex, $"An error occurred seeding the database with test messages. Error: {ex.Message}");
                }
            }
        });
    }
}

要像在以下测试中一样使用:

_client = new CustomWebApplicationFactory<Startup>(new SeedDataClass()).CreateClient();

这一切都可行,但是我希望将泛型添加到自定义Web应用程序工厂类中,并将此代码移到正在处理的nuget包中,以进行内部测试。

类似这样的东西:

public class CustomWebApplicationFactory<TStartup, TContext>
    : WebApplicationFactory<TStartup> 
    where TStartup : class 
    where TContext : DbContext

我一直坚持如何将SeedDataClass类实例提供/注入到新的通用自定义Web应用程序工厂中。

2 个答案:

答案 0 :(得分:1)

如果您只是想将类似的构造函数改编为CustomWebApplicationFactory<TStartup>类的先前实现

_client = new CustomWebApplicationFactory<Startup>(new SeedDataClass()).CreateClient();

然后您的新构造函数将如下所示:

public class CustomWebApplicationFactory<TStartup, TContext> : WebApplicationFactory<TStartup> 
    where TStartup : class 
    where TContext : DbContext
{
    private readonly SeedDataClass _seed;

    public CustomWebApplicationFactory(SeedDataClass seed)
    {
        if (seed == null) throw new ArgumentNullException(nameof(seed));

        _seed = seed;
    }
}

然后像这样更新对构造函数的调用

new CustomWebApplicationFactory<Startup, YourDbContext>(new SeedDataClass()).CreateClient();

答案 1 :(得分:0)

这就是我要去的地方

修正工厂:

public class GenericWebApplicationFactory<TStartup, TContext, TSeed>
    : WebApplicationFactory<TStartup>
    where TStartup : class
    where TContext : DbContext
    where TSeed : class, ISeedDataClass
{
    protected override void ConfigureWebHost(IWebHostBuilder builder)
    {
        base.ConfigureWebHost(builder);
        builder.UseEnvironment("Development");
        builder.ConfigureServices(services =>
        {
            var serviceProvider = new ServiceCollection()
                .AddEntityFrameworkInMemoryDatabase()
                .BuildServiceProvider();

            services.AddSingleton<ISeedDataClass,TSeed >();

            services.AddDbContextPool<TContext>(options =>
            {
                options.UseInMemoryDatabase("InMemoryDbForTesting");
                options.UseInternalServiceProvider(serviceProvider);
                options.EnableSensitiveDataLogging();
            });

            var sp = services.BuildServiceProvider();
            using (var scope = sp.CreateScope())
            {
                var scopedServices = scope.ServiceProvider;
                var db = scopedServices.GetRequiredService<TContext>();
                var logger = scopedServices.GetRequiredService<ILogger<GenericWebApplicationFactory<TStartup, TContext, TSeed>>>();

                var seeder = scopedServices.GetRequiredService<ISeedDataClass>();

                db.Database.EnsureCreated();

                try
                {
                    seeder.InitializeDbForTests();
                }
                catch (Exception ex)
                {
                    logger.LogError(ex, $"An error occurred seeding the database with test messages. Error: {ex.Message}");
                }
            }
        });
    }
}

推荐用法:

    _client = new GenericWebApplicationFactory<Startup, GatewayContext, SeedDataClass>().CreateClient();

带有示例种子类:

public interface ISeedDataClass
{
    void InitializeDbForTests();
}

public class SeedDataClass : ISeedDataClass
{
    private readonly GatewayContext _db;

    public SeedDataClass(GatewayContext db)
    {
        _db = db;
    }

    public void InitializeDbForTests()
    {
        _db.Users.AddRange(
            // add some users here
        );

        _db.SaveChanges(true);
    }
}

现在,我可以在内存数据库中播种种子,但是我认为适合每个应用项目,并且我的GenericWebApplicationFactory现在可以放入helper lib / nuget包中,可以在其他项目中重新使用。