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().All()
.Do(b => b.BlogID = )
.Build(); var fakePosts = Builder<BlogPost>.CreateListOfSize().All()
.Do(x => x.Id = )
.TheFirst().With(x => x.BlogSite = blogSites[])
.TheNext().With(x => x.BlogSite = blogSites[])
.TheNext().With(x => x.BlogSite = blogSites[])
.Build(); efUnitOfWork.AddRange(fakePosts);
efUnitOfWork.SaveChanges(); return fakePosts;
}
}
}

ASP.NET Core 集成测试中结合 WebApplicationFactory 使用 SQLite 内存数据库的更多相关文章

  1. ASP.NET Core 集成测试中通过 Serilog 向控制台输出日志

    日志是程序员的雷达,不仅在生产环境中需要,在集成测试环境中也需要,可以在持续集成失败后帮助定位问题.与生产环境不同,在集成测试环境中使用控制台输出日志更方便,这样可以通过持续集成 runner 执行 ...

  2. ASP.NET Core 集成测试中模拟登录用户的一种姿势

    不管哪种用户验证方式,最终都是在验证成功后设置 HttpContext.User ,后续处理环节通过 HttpContext.User 获取用户信息.如果能直接修改 HttpContext.User ...

  3. 如何在 ASP.NET Core 测试中操纵时间?

    有时候,我们会遇到一些跟系统当前时间相关的需求,例如: 只有开学季才允许录入学生信息 只有到了晚上或者周六才允许备份博客 注册满 3 天的用户才允许进行一些操作 某用户在 24 小时内被禁止发言 很显 ...

  4. 通过重建Hosting系统理解HTTP请求在ASP.NET Core管道中的处理流程[下]:管道是如何构建起来的?

    在<中篇>中,我们对管道的构成以及它对请求的处理流程进行了详细介绍,接下来我们需要了解的是这样一个管道是如何被构建起来的.总的来说,管道由一个服务器和一个HttpApplication构成 ...

  5. ASP.NET Core MVC 中的 [Controller] 和 [NonController]

    前言 我们知道,在 MVC 应用程序中,有一部分约定的内容.其中关于 Controller 的约定是这样的. 每个 Controller 类的名字以 Controller 结尾,并且放置在 Contr ...

  6. 如何在ASP.NET Core应用中实现与第三方IoC/DI框架的整合?

    我们知道整个ASP.NET Core建立在以ServiceCollection/ServiceProvider为核心的DI框架上,它甚至提供了扩展点使我们可以与第三方DI框架进行整合.对此比较了解的读 ...

  7. ASP.NET Core MVC 中设置全局异常处理方式

    在asp.net core mvc中,如果有未处理的异常发生后,会返回http500错误,对于最终用户来说,显然不是特别友好.那如何对于这些未处理的异常显示统一的错误提示页面呢? 在asp.net c ...

  8. ASP.NET Core中的缓存[1]:如何在一个ASP.NET Core应用中使用缓存

    .NET Core针对缓存提供了很好的支持 ,我们不仅可以选择将数据缓存在应用进程自身的内存中,还可以采用分布式的形式将缓存数据存储在一个“中心数据库”中.对于分布式缓存,.NET Core提供了针对 ...

  9. 006.Adding a controller to a ASP.NET Core MVC app with Visual Studio -- 【在asp.net core mvc 中添加一个控制器】

    Adding a controller to a ASP.NET Core MVC app with Visual Studio 在asp.net core mvc 中添加一个控制器 2017-2-2 ...

随机推荐

  1. Codeforces 1096F(dp + 树状数组)

    题目链接 题意: 对于长度为$n$的排列,在已知一些位的前提下求逆序对的期望 思路: 将答案分为$3$部分 $1.$$-1$与$-1$之间对答案的贡献.由于逆序对考虑的是数字之间的大小关系,故假设$- ...

  2. BZOJ 5093: [Lydsy1711月赛]图的价值

    第二类斯特林数模版题 需要一些组合数的小$ trick$ upd:这里更新了本题巧妙的$ O(k)$做法,虽然常数很大就是了 传送门:here 题意:求所有$ n$个节点的无重边自环图的价值和,定义一 ...

  3. JAVA进阶7

    间歇性混吃等死,持续性踌躇满志系列-------------第7天 1.Map接口的常用方法 import java.util.HashMap; import java.util.Map; publi ...

  4. 深度学习在graph上的使用

    原文地址:https://zhuanlan.zhihu.com/p/27216346 本文要介绍的这一篇paper是ICML2016上一篇关于 CNN 在图(graph)上的应用.ICML 是机器学习 ...

  5. filebeat_config

    Filebeat Prospector filebeat.prospectors: - input_type: log paths: - /var/log/apache/httpd-*.log doc ...

  6. Nikto and whatweb

    root@kali:~# nikto -host www.baidu.com- Nikto v2.1.6------------------------------------------------ ...

  7. 学习笔记: yield迭代器

    yield 与 IEnumerable<T> 结对出现, 可实现按需获取 , 迭代器模式 static void Main(string[] args)         {         ...

  8. Javascript我学之二函数定义

    本文是金旭亮老师网易云课堂的课程笔记,记录下来,以供备忘 函数 几个要点:                 a).函数是javascript中的一等公民 (重要性)                 b ...

  9. log4j2使用入门(一)

    log4j2是log4j的一个升级版,与log4j1相比进行了很大的改善,同时也修复了一些logback的架构上的问题.所以是目前应用开发的首选的日志器(下载位置:http://apache.faye ...

  10. django——会话追踪技术

    1.引言 1.1什么是会话追踪技术 会话是指一个终端用户(服务器)与交互系统(客户端)进行通讯的过程. 1.2 什么是会话跟踪 对同一个用户对服务器的连续的请求和接受响应的监视.(将用户与同一用户发出 ...