# Unità di test Umami.Net - Testing UmamiClient

# Introduzione

Ora ho [Umami.Net package](https://www.nuget.org/packages/Umami.Net/) Cioè, ovviamente, voglio assicurarmi che tutto funzioni come previsto. Per farlo il modo migliore è testare in modo comprensivo tutte le metodi e le classi. Qui arriva l'unità di test.
Note: Questa non è una ' un ' approccio perfettoM SK3 un post di tipo, ma ' è proprio come l'ho fatto ioMSC6lo ho fatto in questo momentoMST7 In realtà non c'è davvero bisogno di far ridere il `IHttpMessageHandler` qui potete attaccare un DelegatingMessageHandler ad un normale HttpClient per farlo. Volevo solo mostrarvi come si può fare con un Mock.

[TOC]

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

# Test di unità

L'unità di test si riferisce al processo di testare singole unità di codice per assicurarsi che funzionino come si aspettava. Questo viene fatto scrivendo test che chiamano i metodi e le classi in modo controllato e poi controllando se il risultato è quello che si aspetta.

Per un pacchetto come Umami.Net questo è un po' complicato perché entrambi chiamano un cliente remoto. `HttpClient` e ha un `IHostedService` usa per rendere il trasferimento di nuovi dati di evento il più flessibile possibile.

## Testando UmamiClient

La parte più importante del test `HttpClient` La biblioteca basata evita l'esatto 'HttpClient' callM SK2 Questo è fatto creando un `HttpClient` che usa un `HttpMessageHandler` che restituisce una risposta nota. Questo viene fatto creando un `HttpClient` con un `HttpMessageHandler` che restituisce una risposta conosciuta; in questo caso faccio eco alla risposta di input e cerco che' non sia stato alterato dal `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;
    }
```

Come vedete' questo crea un `Mock<HttpMessageHandler>` Dopodiché passo all'università. `UmamiClient`.
In questo codice lo inserisco nel nostro computer. `IServiceCollection` metodo di montaggio. Questo aggiunge tutti i servizi necessari dal `UmamiClient` compresa la nostra nuova `HttpMessageHandler` e poi ritorna il `IServiceCollection` per l'uso nei test.

```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;
    }
```

Per usarlo e iniettarlo nel `UmamiClient` Poi uso questi servizi. `UmamiClient` configurazione.

```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>();
    }
```

Vedrete che qui ho un sacco di parametri opzionali alternativi che mi permettono di inserire diverse opzioni per diversi tipi di test.

### I test

Quindi adesso ho tutto questo sistema sul posto e ora posso iniziare a scrivere test per `UmamiClient` metodi.

#### Invia

Questo setup significa che i nostri test possono essere molto semplici.

```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);
    }
}
```

Qui vedete il caso di prova più semplice, assicurandovi solo che la `UmamiClient` Possiamo mandare un messaggio e ottenere una risposta; importante è che testiamo anche un caso di eccezione dove il `type` è sbagliato. Questa è una parte spesso superata dell'esame, assicurando che il codice fallisca come si aspettava

#### Vedere la pagina

Per testare il nostro metodo di visione della pagina possiamo fare qualcosa di simile. Nel codice sotto uso il mio `EchoHttpHandler` per riflettere la risposta inviata e assicurarsi che rimanda quello che mi aspettavo.

```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);
    }
```

### HttpContextAccessor

Si usa la `HttpContextAccessor` per stabilire il percorso. `/testpath` E poi controlla che il `UmamiClient` lo manda correttamente.

```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;
    }

```

Questo è importante per il nostro codice cliente Umami perché gran parte dei dati inviati da ogni richiesta viene effettivamente generato dinamicamente dal `HttpContext` object. Quindi non possiamo mandare niente in un `await umamiClient.TrackPageView();` e manderà ancora i dati corretti estratto dall'Url. `HttpContext`.

Come vedremo più tardi, è anche importante che la meraviglia spedisca oggetti come questo. `UserAgent` e `IPAddress` Questi vengono usati dal server Umami per tracciare i dati e 'trackM SK1 visualizzazioni degli utenti senza usare cookies.

Per avere questa prevedibilità definiamo un gruppo di conti in `Consts` class. Così possiamo testare contro risposte e richieste prevedibili.

```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";
}
```

## Further testing

Questo è solo l'inizio della nostra strategia di test per Umami.Net, dobbiamo ancora testare la `IHostedService` e testare contro i dati reali Umami genera ( che non è documentato da nessuna parte ma contiene un token JWT con alcuni dati utili.

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

Quindi noi ' vorremo testare per questo , simulare il token e forse restituire i dati su ogni visita. `uuid(websiteId,ipaddress, useragent)`).

# Conclusione

Questo è solo l'inizio del test di Umami.Net package , ci sono ancora molti altri lavori da fare, ma questo è un buon inizio. IoM SK4 aggiungerò più test mentre andrò e non c'è dubbio che migliorierò questi.