This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Umami.Net MS K1测试UmmiClient单位测试
Sunday, 01 September 2024
一. 导言导言 导言 导言 导言
现在我有了 Ummami.+Net 套件 我当然希望确保一切工作都如预期的那样有效 . 这样做的最好方法就是全面测试所有的方法和等级
注意: 这是'}这是'}这不是一个“MSK2”的完美方法'}{,}_BARBAR_MSK IHttpMessageHandler a 在这里您可以攻击一位女主人 Handler 成为普通的 HttpClient 来做这个 .}我只是想展示你如何用一个 Mok. 来做到这一点。
单位测试
单位测试是指单个编码单位的测试过程,以确保它们如预期的那样工作。 这是通过写作检测完成的,用控制的方式将方法和等级调用起来,然后按照预期检查输出=.}
对于像 UmamiMSMK0}Net 这样的软件包来说,当它同时调用远程客户端时,这是一个多么棘手的问题。 HttpClient 并且有一个 IHostedService 它用来使新事件数据的发送尽可能无缝\ .
UmamiClient测试
试验的主要部分a HttpClient 新建库正在避免实际的 @' @HttpClient@'}拨号: .}这样可以创建 HttpClient 使用 a HttpMessageHandler 返回已知响应@.} 这将通过创建 HttpClient 与 a 有 HttpMessageHandler 返回已知的响应@;}在此情况下,我只回回输入回复并检查是否没有被 UmamiClient.
public static HttpMessageHandler Create()
{
var mockHandler = new Mock<HttpMessageHandler>();
mockHandler.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.Is<HttpRequestMessage>(x => x.RequestUri.ToString().Contains("api/send")),
ItExpr.IsAny<CancellationToken>())
.ReturnsAsync((HttpRequestMessage request, CancellationToken cancellationToken) =>
{
// Read the request content
var requestBody = request.Content != null
? request.Content.ReadAsStringAsync(cancellationToken).Result
: null;
// Create a response that echoes the request body
var responseContent = requestBody != null
? requestBody
: "No request body";
// Return the response
return new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(responseContent, Encoding.UTF8, "application/json")
};
});
return mockHandler.Object;
}
当你看到这个设置 Mock<HttpMessageHandler> 然后,我进入 UmamiClient.
在这个代码中,我把这个插进我们的 IServiceCollection 设置方法@. 这将添加所有需要的服务 UmamiClient 包括我们新的 HttpMessageHandler 然后返回 IServiceCollection 用于测试的“.”
public static IServiceCollection SetupServiceCollection(string webSiteId = Consts.WebSiteId,
string umamiPath = Consts.UmamiPath, HttpMessageHandler? handler = null)
{
var services = new ServiceCollection();
var umamiClientSettings = new UmamiClientSettings
{
WebsiteId = webSiteId,
UmamiPath = umamiPath
};
services.AddSingleton(umamiClientSettings);
services.AddScoped<PayloadService>();
services.AddLogging(x => x.AddConsole());
// Mocking HttpMessageHandler with Moq
var mockHandler = handler ?? EchoMockHandler.Create();
services.AddHttpClient<UmamiClient>((serviceProvider, client) =>
{
var umamiSettings = serviceProvider.GetRequiredService<UmamiClientSettings>();
client.BaseAddress = new Uri(umamiSettings.UmamiPath);
}).ConfigurePrimaryHttpMessageHandler(() => mockHandler);
return services;
}
将它注入到 UmamiClient 然后,我使用这些服务 在 UmamiClient 设置@. @% 1
public static UmamiClient GetUmamiClient(IServiceCollection? serviceCollection = null,
HttpContextAccessor? contextAccessor = null)
{
serviceCollection ??= SetupServiceCollection();
SetupUmamiClient(serviceCollection, contextAccessor);
if (serviceCollection == null) throw new NullReferenceException(nameof(serviceCollection));
var serviceProvider = serviceCollection.BuildServiceProvider();
return serviceProvider.GetRequiredService<UmamiClient>();
}
您将看到我这里有一堆备选参数, 允许我为不同的测试类型输入不同选项@. @%
测试
所以现在我把所有这些设置都安排好了 我现在可以开始为 UmamiClient .
发送
这些设置意味着 我们的测试其实很简单
public class UmamiClient_SendTests
{
[Fact]
public async Task Send_Wrong_Type()
{
var umamiClient = SetupExtensions.GetUmamiClient();
await Assert.ThrowsAsync<ArgumentException>(async () => await umamiClient.Send(type: "boop"));
}
[Fact]
public async Task Send_Empty_Success()
{
var umamiClient = SetupExtensions.GetUmamiClient();
var response = await umamiClient.Send();
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
}
这里您可以看到最简单的测试案例@ , @ 只是确保 UmamiClient 关键是我们也可以测试一个例外案例。 type 这是测试中经常被忽略的部分... ... ,...
页面视图视图
为了测试我们的页面浏览方法 我们可以做类似的东西 在下面的代码里,我用我的 EchoHttpHandler 来回想一下所发送的回复,并确保它将我期待的东西寄回来。
[Fact]
public async Task TrackPageView_WithNoUrl()
{
var defaultUrl = "/testpath";
var contextAccessor = SetupExtensions.SetupHttpContextAccessor(path: "/testpath");
var umamiClient = SetupExtensions.GetUmamiClient(contextAccessor: contextAccessor);
var response = await umamiClient.TrackPageView();
var content = await response.Content.ReadFromJsonAsync<EchoedRequest>();
Assert.NotNull(response);
Assert.NotNull(content);
Assert.Equal(content.Payload.Url, defaultUrl);
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
}
HtpConcontext 获取器
此处使用 HttpContextAccessor 将路径设置为 /testpath 然后检查 UmamiClient 正确发送@ . @
public static HttpContextAccessor SetupHttpContextAccessor(string host = Consts.Host,
string path = Consts.Path, string ip = Consts.Ip, string userAgent = Consts.UserAgent,
string referer = Consts.Referer)
{
HttpContext httpContext = new DefaultHttpContext();
httpContext.Request.Host = new HostString(host);
httpContext.Request.Path = new PathString(path);
httpContext.Connection.RemoteIpAddress = IPAddress.Parse(ip);
httpContext.Request.Headers.UserAgent = userAgent;
httpContext.Request.Headers.Referer = referer;
var context = new HttpContextAccessor { HttpContext = httpContext };
return context;
}
这对于我们的Umami客户代码很重要,因为从每项请求中发送的数据大多实际上是动态生成的。 HttpContext 对象@. @}所以我们完全不能发送任何文件 await umamiClient.TrackPageView(); 并且它仍然会通过从电源中提取Url来发送正确的数据 HttpContext.
当我们稍后看到它的时候 ' UserAgent 和 IPAddress 当Umami 服务器在不使用 cookie@.}情况下跟踪数据时,
为了有这个可以预测的,我们定义 一群Consts在 Consts @.#所以我们可以根据可预测的回应和请求来测试_.}
public class Consts
{
public const string UmamiPath = "https://example.com";
public const string WebSiteId = "B41A9964-FD33-4108-B6EC-9A6B68150763";
public const string Host = "example.com";
public const string Path = "/example";
public const string Ip = "127.0.0.1";
public const string UserAgent = "Test User Agent";
public const string Referer = "Test Referer";
public const string DefaultUrl = "/testpath";
public const string DefaultTitle = "Example Page";
public const string DefaultName = "RSS";
public const string DefaultType = "event";
public const string Email = "[email protected]";
public const string UserId = "11224456";
public const string UserName = "Test User";
public const string SessionId = "B41A9964-FD33-4108-B6EC-9A6B68150763";
}
进一步测试
这仅仅是我们测试Ummi. Net,战略的开始, IHostedService 并对照实际数据测试 Umami 生成了 @(}这是'}记录在任何地方,但含有 JWT 标记和一些有用的数据@.)}*
{
"alg": "HS256",
"typ": "JWT"
}{
"id": "b9836672-feee-55c5-985a-a5a23d4a23ad",
"websiteId": "32c2aa31-b1ac-44c0-b8f3-ff1f50403bee",
"hostname": "example.com",
"browser": "chrome",
"os": "Windows 10",
"device": "desktop",
"screen": "1920x1080",
"language": "en-US",
"country": "GB",
"subdivision1": null,
"subdivision2": null,
"city": null,
"createdAt": "2024-09-01T09:26:14.418Z",
"visitId": "e7a6542f-671a-5573-ab32-45244474da47",
"iat": 1725182817
}2|Y*: �(N%-ޘ^1>@V
因此,我们将想要测试是否要模拟此符号并可能返回每次访问的数据 : @(@as you'}当您回忆这是来自 uuid(websiteId,ipaddress, useragent)).
在结论中的结论
这仅仅是测试Umami. @Net profession@,}这里还有很多事情要做 但是这是一个良好的开始