记录一下对 ASP.NET Core Middleware 进行单元测试的代码
Middleware 实现的功能是从请求的 hostname 中读取 subdomain,然后根据 blog 的配置,进行 url 重写或者重定向,实现代码如下
public class SubdomainMiddleware(
RequestDelegate next,
IBlogQueryService blogQuery,
IOptions<SiteOptions> siteOptions)
{
public async Task InvokeAsync(HttpContext context)
{
var request = context.Request;
var currentHost = request.Host.Host.AsSpan();
var defaultHost = new HostString(siteOptions.Value.Host);
if (CommonUrlHelper.TryParseBlogAppFromHostname(currentHost, out var blogApp))
{
var path = request.Path.Value;
if (await blogQuery.IsSubdomainBlog(blogApp))
{
request.Host = defaultHost;
path = path == "/" ? string.Empty : request.Path;
request.Path = $"/{blogApp}{path}";
}
else
{
var newUrl = UriHelper.BuildAbsolute("https", defaultHost, request.PathBase, $"/{blogApp}{request.Path}", request.QueryString);
context.Response.Redirect(newUrl, false);
return;
}
}
await next(context);
}
}
基于 TestServer 构建 middlewrite pipeline 的代码
private static async Task<WebApplication> BuildApp()
{
var builder = WebApplication.CreateBuilder();
MockBlogQueryService(builder.Services);
MockSiteOptions(builder.Services);
builder.WebHost.UseTestServer();
var app = builder.Build();
app.UseMiddleware<SubdomainMiddleware>();
app.Run(async context => await context.Response.WriteAsync(context.Request.GetDisplayUrl()));
await app.StartAsync();
return app;
}
Mock 中间件所用到的接口
private static void MockBlogQueryService(IServiceCollection services)
{
services.Mock<IBlogQueryService>(
mock => mock.Setup(x => x.IsSubdomainBlog("dudu")).ReturnsAsync(true),
ServiceLifetime.Scoped);
}
private static void MockSiteOptions(IServiceCollection services)
{
services.Mock<IOptions<SiteOptions>>(
mock => mock.Setup(x => x.Value).Returns(new SiteOptions()),
ServiceLifetime.Scoped);
}
Url 重写的测试用例
[Fact]
public async Task Rewrite_subdomain_blog_url()
{
// Arrange
await using var app = await BuildApp();
// Act
var client = app.GetTestClient();
var response = await client.GetAsync("https://dudu.cnblogs.com/p/18637966");
// Assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal("https://www.cnblogs.com/dudu/p/18637966", await response.Content.ReadAsStringAsync());
}
Url 重定向的测试用例
[Fact]
public async Task Redirect_non_subdomain_blog()
{
// Arrange
await using var app = await BuildApp();
// Act
var client = app.GetTestClient();
var response = await client.GetAsync("https://cmt.cnblogs.com/p/11391410.html");
// Assert
Assert.Equal(HttpStatusCode.Redirect, response.StatusCode);
Assert.Equal("https://www.cnblogs.com/cmt/p/11391410.html", response.Headers.Location.ToString());
}
浙公网安备 33010602011771号