zoukankan      html  css  js  c++  java
  • ASP.NET Core 集成测试中结合 WebApplicationFactory 使用 SQLite 内存数据库

    SQLite 内存数据库(in-memory database)的连接字符串是  Data Source=:memory: ,它的特点是数据库连接一关闭,数据库就会被删除。而使用  services.AddDbContext 通过连接字符串配置 EF Core 时,EF Core 会在每次查询或 SaveChanges 后立即关闭数据库连接。在这样的情况下,集成测试中就无法在向 SQLite 内存数据库写入数据库后进行查询测试。

    为了解决上述问题,我们就不能让 EF Core 自己自动维护数据库连接,而只能改为手动模式,手工创建并打开 SqliteConnection 给 EF Core 使用,在用完之后的适当时候关闭连接。

    除此之外,由于在每次打开数据库连接都会创建新的数据库,所以还要解决在什么写入数据之前完成数据库的初始化。

    结合 WebApplicationFactory ,我们用下面继承自 WebApplicationFactory 的实现代码解决了问题。

    public class BlogWebAppFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
    {
        private DbConnection _dbConnection;
    
        public BlogWebAppFactory()
        { }
    
        protected override void ConfigureWebHost(IWebHostBuilder builder)
        {
            base.ConfigureWebHost(builder);
    
            builder.ConfigureServices(services =>
            {
                _dbConnection = new SqliteConnection("Data Source=:memory:");
                _dbConnection.Open();
                services.AddDbContext<EfUnitOfWork>(options =>
                {
                    options.UseSqlite(_dbConnection);
                });
            });
        }
    
        protected override TestServer CreateServer(IWebHostBuilder builder)
        {
            var server = base.CreateServer(builder);
    
            using (var scope = server.Host.Services.CreateScope())
            {
                var dbContext = scope.ServiceProvider.GetRequiredService<EfUnitOfWork>();
                dbContext.Database.EnsureCreated();
            }
    
            return server;
        }
    
        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            _dbConnection?.Dispose();
        }
    }

    集成测试中的示例代码如下

    public class PostsWebApiTests : IClassFixture<BlogWebAppFactory<Startup>>
    {
        private readonly BlogWebAppFactory<Startup> _factory;
        private readonly HttpClient _httpClient;
    
        public PostsWebApiTests(BlogWebAppFactory<Startup> factory)
        {
            _factory = factory;
            _httpClient = factory.CreateClient();
        }
    
        [Fact]
        public async Task GetPostsByBlogIdsTest()
        {
            var fakePosts = SeedData();
            var blogIds = fakePosts.Select(p => p.BlogID).Distinct();           
            var response = await _httpClient.PostAsJsonAsync($"/blogposts/blogIds", blogIds);
            Assert.Equal(HttpStatusCode.OK, response.StatusCode);
        }
    
        private IList<BlogPost> SeedData()
        {
            using (var scope = _factory.Server.Host.Services.CreateScope())
            {
                var efUnitOfWork = scope.ServiceProvider.GetRequiredService<EfUnitOfWork>();
    
                var blogSites = Builder<BlogSite>.CreateListOfSize(3).All()
                .Do(b => b.BlogID = 0)
                .Build();
    
                var fakePosts = Builder<BlogPost>.CreateListOfSize(12).All()
                .Do(x => x.Id = 0)
                .TheFirst(4).With(x => x.BlogSite = blogSites[0])
                .TheNext(4).With(x => x.BlogSite = blogSites[1])
                .TheNext(4).With(x => x.BlogSite = blogSites[2])
                .Build();
    
                efUnitOfWork.AddRange(fakePosts);
                efUnitOfWork.SaveChanges();
    
                return fakePosts;
            }
        }
    }
  • 相关阅读:
    VM安装CentOs7虚拟机后无法上网之解决方法
    vue中touchEnd事件和touchStart、touchMove获取坐标不一样,IOS绑定touch事件失效
    【个推独家】让你一次性掌握Neo4j性能优化秘籍的三大狠招
    【深度干货】异构数据的SQL一站式解决方案
    个推基于Jenkins的自动打包构建实践
    个推CTO叶新江专访| 数据智能的未来,是不提大数据但其无所不在的时代
    应用系统间数据传输方式总结(附相关概念解释)
    JavaScript中“&&”和“||”操作符的意义,深入理解和使用场景
    静态布局、自适应布局、流式布局、响应式布局、弹性布局等的概念和区别
    前端跨域问题相关知识详解(原生js和jquery两种方法实现jsonp跨域)
  • 原文地址:https://www.cnblogs.com/dudu/p/9765937.html
Copyright © 2011-2022 走看看