我在我的 github 存储库中制作了一个最小化代码来说明问题:https://github.com/suugbut/MiniTest/tree/main。
我正在学习集成测试来测试我的最小 API。我无法用用于集成测试的内存数据库替换生产的
AppDbContext
设置。我收到以下错误:
Api.Test.TodoEndpoint_IntegrationTest.NumberOfTodos_MustBe_Two
Source: TodoEndpoint_IntegrationTest.cs line 44
Duration: 1.1 sec
Message:
Microsoft.Data.Sqlite.SqliteException : SQLite Error 1: 'no such table: Todos'.
Stack Trace:
SqliteException.ThrowExceptionForRC(Int32 rc, sqlite3 db)
SqliteCommand.PrepareAndEnumerateStatements()+MoveNext()
SqliteCommand.GetStatements()+MoveNext()
SqliteDataReader.NextResult()
SqliteCommand.ExecuteReader(CommandBehavior behavior)
SqliteCommand.ExecuteDbDataReader(CommandBehavior behavior)
RelationalCommand.ExecuteReader(RelationalCommandParameterObject parameterObject)
Enumerator.InitializeReader(Enumerator enumerator)
<>c.<MoveNext>b__21_0(DbContext _, Enumerator enumerator)
NonRetryingExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
Enumerator.MoveNext()
Enumerable.TryGetSingle[TSource](IEnumerable`1 source, Boolean& found)
lambda_method157(Closure, QueryContext)
QueryCompiler.Execute[TResult](Expression query)
EntityQueryProvider.Execute[TResult](Expression expression)
TodoEndpoint_IntegrationTest.NumberOfTodos_MustBe_Two() line 50
RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
AppDbContext
用于生产var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<AppDbContext>(options =>
{
var constr = builder.Configuration.GetConnectionString("DefaultConnection");
options.UseSqlite(constr);
});
builder.Services.AddScoped<TodoRepo>();
var app = builder.Build();
// Others are removed for the sake of simplicity.
"ConnectionStrings": {
"DefaultConnection": "DataSource=Api.db"
}
如果您想检查
Program.cs
,请导航至 https://github.com/suugbut/MiniTest/blob/main/Api/Program.cs。
AppDbContext
用于集成测试public sealed class CustomWebApplicationFactory : WebApplicationFactory<Program>
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
base.ConfigureWebHost(builder);
builder.ConfigureServices(isc =>
{
var descriptor = isc.SingleOrDefault(d => d.ServiceType == typeof(DbContextOptions<AppDbContext>));
if (descriptor != null)
{
isc.Remove(descriptor);
}
isc.AddDbContext<AppDbContext>(options =>
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
options.UseSqlite(connection);
});
});
}
// Others are removed for the sake of simplicity.
}
如果您想检查
CustomWebApplicationFactory.cs
,请导航至 https://github.com/suugbut/MiniTest/blob/main/Api.Test/CustomWebApplicationFactory.cs
// Dependencies are removed for the sake of simplicity.
[Theory]
[InlineData(1)]
[InlineData(2)]
public async Task GetTodoById_Returns_OK(int id)
{
// act
var response = await _client.GetAsync($"/todos/{id}");
// assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
var content = await response.Content.ReadAsStringAsync();
Assert.NotNull(content);
}
[Theory]
[InlineData(3)]
public async Task GetTodoById_Returns_NotFound(int id)
{
// act
var response = await _client.GetAsync($"/todos/{id}");
// assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}
[Fact]
public void NumberOfTodos_MustBe_Two()
{
using (var scope = _factory.Services.CreateScope())
{
if (scope.ServiceProvider.GetRequiredService<AppDbContext>() is AppDbContext context)
{
var count = context.Todos.Count();
Assert.Equal(2, count);
}
}
}
您还可以在 https://github.com/suugbut/MiniTest/blob/main/Api.Test/TodoEndpoint_IntegrationTest.cs
检查此测试以下:
isc.AddDbContext<AppDbContext>(options =>
{
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
options.UseSqlite(connection);
});
将导致多次创建新连接,并且默认情况下 SQLite 内存中不共享。有多种选项可以解决此问题,例如只需将连接创建移到 lambda 之外:
var connection = new SqliteConnection("DataSource=:memory:");
connection.Open();
isc.AddDbContext<AppDbContext>(options =>
{
options.UseSqlite(connection);
});