Back to "Unità di test Umami.Net - Testing UmamiClient"

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 xUnit

Unità di test Umami.Net - Testing UmamiClient

Sunday, 01 September 2024

Introduzione

Ora ho Umami.Net package 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.

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.

    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.

    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.

    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.

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.

    [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.

    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.

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

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.

{
  "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.

logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.