c# - 如何确保每个 xunit 测试都有一个新的 InMemory 种子数据库?
问题描述
我正在使用 xunit 为我的 Asp.Net Core API 设置集成测试。到目前为止,我有两个测试设置:
public class BasicTests : IClassFixture<CustomWebApplicationFactory<EcommerceWebAPI.Startup>>
{
private readonly CustomWebApplicationFactory<EcommerceWebAPI.Startup> _factory;
public BasicTests(CustomWebApplicationFactory<EcommerceWebAPI.Startup> factory)
{
_factory = factory;
}
[Theory]
[InlineData("84.247.85.224")]
public async Task AuthenticateUserTest(string ip)
{
// Arrange
var client = _factory.CreateClient();
int responseStatusCode = 0;
// Act
var request = new HttpRequestMessage(HttpMethod.Post, "/api/Users/AuthenticateUser")
{
Content = new StringContent("{\"Username\":\"test@test.com\",\"Password\":\"test\"}", Encoding.UTF8,
"application/json")
};
request.Headers.Add("X-Real-IP", ip);
var response = await client.SendAsync(request);
responseStatusCode = (int)response.StatusCode;
// Assert
Assert.Equal(200, responseStatusCode);
}
[Theory]
[InlineData("84.247.85.224")]
[InlineData("84.247.85.225")]
[InlineData("84.247.85.226:6555")]
[InlineData("205.156.136.211, 192.168.29.47:54610")]
public async Task SpecificIpRule(string ip)
{
// Arrange
var client = _factory.CreateClient();
int responseStatusCode = 0;
// Act
for (int i = 0; i < 4; i++)
{
var request = new HttpRequestMessage(HttpMethod.Post, "/api/Users/AuthenticateUser")
{
Content = new StringContent("{\"Username\":\"Test\",\"Password\":\"Test\"}", Encoding.UTF8,
"application/json")
};
request.Headers.Add("X-Real-IP", ip);
var response = await client.SendAsync(request);
responseStatusCode = (int)response.StatusCode;
}
// Assert
Assert.Equal(429, responseStatusCode);
}
}
我的自定义应用程序工厂:
public class CustomWebApplicationFactory<TStartup> : WebApplicationFactory<TStartup> where TStartup : class
{
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureServices(services =>
{
var descriptor = services.SingleOrDefault(
d => d.ServiceType ==
typeof(DbContextOptions<EntityContext>));
services.Remove(descriptor);
services.AddDbContext<EntityContext>(options =>
{
options.UseInMemoryDatabase("InMemoryDbForTesting");
});
var sp = services.BuildServiceProvider();
using (var scope = sp.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var db = scopedServices.GetRequiredService<EntityContext>();
var logger = scopedServices
.GetRequiredService<ILogger<CustomWebApplicationFactory<TStartup>>>();
db.Database.EnsureDeleted();
db.Database.EnsureCreated();
try
{
Utilities.InitializeDbForTests(db);
}
catch (Exception ex)
{
logger.LogError(ex, "An error occurred seeding the " +
"database with test messages. Error: {Message}", ex.Message);
}
}
});
}
}
我为数据库播种:
static class Utilities
{
public static void InitializeDbForTests(EntityContext db)
{
var customer = new Customer
{
Custnmbr = "AARONFIT0001"
};
db.Customers.Add(customer);
var user = new User
{
Id = 1,
Customer = customer,
EmailAddress = "test@test.com",
PasswordHash = BCrypt.Net.BCrypt.HashPassword("test")
};
db.Users.Add(user);
db.SaveChanges();
}
}
两个测试用例都通过了。但是,当第二个测试尝试再次运行种子时,我在日志中得到一个实体已经存在异常。我认为把这条线db.Database.EnsureDeleted()
放在我CustomWebApplicationFactory
的身上可以确保每次测试都从一个全新的 InMemory 数据库开始。如何确保每次测试都有一个新的 InMemory 种子数据库?
解决方案
经过一些额外的搜索,一篇特定的 StackOverflow 文章在集成测试之间重置内存数据库使我得出了这个结论。我需要为每个测试初始化工厂...
public class AuthenticationTests
{
[Theory]
[InlineData("84.247.85.224")]
public async Task AuthenticateUserTest(string ip)
{
// Arrange
using var factory = new CustomWebApplicationFactory<EcommerceWebAPI.Startup>();
var client = factory.CreateClient();
int responseStatusCode = 0;
// Act
var request = new HttpRequestMessage(HttpMethod.Post, "/api/Users/AuthenticateUser")
{
Content = new StringContent("{\"Username\":\"test@test.com\",\"Password\":\"test\"}", Encoding.UTF8,
"application/json")
};
request.Headers.Add("X-Real-IP", ip);
var response = await client.SendAsync(request);
responseStatusCode = (int)response.StatusCode;
// Assert
Assert.Equal(200, responseStatusCode);
}
[Theory]
[InlineData("84.247.85.224")]
[InlineData("84.247.85.225")]
[InlineData("84.247.85.226:6555")]
[InlineData("205.156.136.211, 192.168.29.47:54610")]
public async Task SpecificIpRule(string ip)
{
// Arrange
using var factory = new CustomWebApplicationFactory<EcommerceWebAPI.Startup>();
var client = factory.CreateClient();
int responseStatusCode = 0;
// Act
for (int i = 0; i < 4; i++)
{
var request = new HttpRequestMessage(HttpMethod.Post, "/api/Users/AuthenticateUser")
{
Content = new StringContent("{\"Username\":\"Test\",\"Password\":\"Test\"}", Encoding.UTF8,
"application/json")
};
request.Headers.Add("X-Real-IP", ip);
var response = await client.SendAsync(request);
responseStatusCode = (int)response.StatusCode;
}
// Assert
Assert.Equal(429, responseStatusCode);
}
}
Heres希望SO社区可以提出一个更优雅的解决方案......
推荐阅读
- nagios - Thruk - 使用 API 获取详细信息
- typescript - 使用带有 jest 的 typescript 的 identity-obj-proxy 时返回未定义
- javascript - 如何通过 AJAX 状态码从 C# Webmethod 获取数据?
- java - spring security中如何获取当前用户权限/角色并根据角色做一些计算
- express - 成功注册后,我无法登录快递
- perforce - p4 如何识别工作区目录?
- node.js - Twilio 以 PDF 格式获取传真,将其附加到 MailGun 中并使用 request.js 获取 - 未获取正确的文件名
- ios - 以编程方式在 tabitem 点击时加载一个导航控制器
- sql - 在 SQL 中选择最小日期或空值
- ios - 有没有一种正确的方法来为使用 CITextImageGenerator 生成的图像着色?