# Umami.Net MS K1测试UmmiClient单位测试

# 一. 导言导言 导言 导言 导言

现在我有了 [Ummami.+Net 套件](https://www.nuget.org/packages/Umami.Net/) 我当然希望确保一切工作都如预期的那样有效 . 这样做的最好方法就是全面测试所有的方法和等级
注意: 这是'}这是'}这不是一个“MSK2”的完美方法'}{,}_BARBAR_MSK `IHttpMessageHandler` a 在这里您可以攻击一位女主人 Handler 成为普通的 HttpClient 来做这个 .}我只是想展示你如何用一个 Mok. 来做到这一点。

[TOC]

<!--category-- xUnit, Umami -->
<datetime class="hidden">2024-09-01T17:22</datetime>

# 单位测试

单位测试是指单个编码单位的测试过程,以确保它们如预期的那样工作。 这是通过写作检测完成的,用控制的方式将方法和等级调用起来,然后按照预期检查输出=.}

对于像 UmamiMSMK0}Net 这样的软件包来说,当它同时调用远程客户端时,这是一个多么棘手的问题。 `HttpClient` 并且有一个 `IHostedService` 它用来使新事件数据的发送尽可能无缝\ .

## UmamiClient测试

试验的主要部分a `HttpClient` 新建库正在避免实际的 @' @HttpClient@'}拨号: .}这样可以创建 `HttpClient` 使用 a `HttpMessageHandler` 返回已知响应@.} 这将通过创建 `HttpClient` 与 a 有 `HttpMessageHandler` 返回已知的响应@;}在此情况下,我只回回输入回复并检查是否没有被 `UmamiClient`.

```csharp
    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` 用于测试的“.”

```csharp
    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

```csharp
    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` .

#### 发送

这些设置意味着 我们的测试其实很简单

```csharp
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` 来回想一下所发送的回复,并确保它将我期待的东西寄回来。

```csharp
    [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` 正确发送@ . @ {}

```csharp
    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` @.#所以我们可以根据可预测的回应和请求来测试_.}

```csharp
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 = "test@test.com";

    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 标记和一些有用的数据@.)}*

```json
{
  "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@,}这里还有很多事情要做 但是这是一个良好的开始