Essai d'unité Umami.Net - Essai du client Umami (Français (French))

Essai d'unité Umami.Net - Essai du client Umami

Sunday, 01 September 2024

//

6 minute read

Introduction

Maintenant, j’ai le Umami.Paquet Net out there I of course want to ensure it all works as expected. Pour ce faire, la meilleure façon est d’essayer assez exhaustivement toutes les méthodes et les classes. Remarque: Ce n’est pas une 'approche parfaite ' post typeM SK4 c’est justement comme je l’ai fait à l’heure actuelle. IHttpMessageHandler ici a vous pouvez attaquer un DelegatingMessageHandler à un HttpClient normal pour le faire. Je voulais seulement montrer comment vous pouvez le faire avec Mock.

Évaluation des unités

L’unité d’essai désigne le processus consistant à tester les unités de code individuelles pour s’assurer qu’elles fonctionnent comme prévu.

Pour un paquet comme Umami.Net, cela est quelque peu compliqué puisqu'il appele un client à distance HttpClient et a un IHostedService il utilise pour rendre l’envoi de nouvelles données d’événement aussi linéaire que possible.

Évaluation du client Umami

La majeure partie de l’essai HttpClient la bibliothèque basée évite l’appel réel 'HttpClient' callM SK2 Ce travail est effectué en créant un HttpClient qui utilise un HttpMessageHandler qui retourne une réponse connue. Ceci est fait en créant un HttpClient avec un HttpMessageHandler qui retourne une réponse connue; dans ce cas, je répète simplement la réponse d’entrée et vérifie que 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;
    }

Comme vous le verrez ci-dessus, cela crée un Mock<HttpMessageHandler> Je passe ensuite dans le UmamiClient. Dans ce code, je mets cela dans notre IServiceCollection méthode de mise en place. Cela ajoute tous les services requis par UmamiClient incluant notre nouveau HttpMessageHandler et ensuite retourne le IServiceCollection pour utilisation dans les essais.

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

Pour l'utiliser et l'injecter dans le UmamiClient Je puis utiliser ces services dans le UmamiClient configuration.

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

Vous verrez que j’ai ici un certain nombre de paramètres facultatifs qui m’autorisent à injecter différentes options pour différents types d’essai.

Les essais

Donc maintenant que j'ai tout ce setup en place je peux maintenant commencer à écrire des tests pour le UmamiClient méthodes.

Envoyer

Tout cela signifie que nos tests peuvent être très simples.

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

Vous voyez ici le cas d’essai le plus simple, tout simplement en s’assurant que UmamiClient peut envoyer un message et obtenir une réponse; important, nous testons également pour un cas d’exception où le type est incorrecte. C'est une partie souvent négligée de l'essai, pour s'assurer que le code ne fonctionne pas comme prévuM SK2

Voir la page

Pour tester notre méthode de visualisation des pages, nous pouvons faire quelque chose similaire. Dans le code ci-dessous j’utilise mon EchoHttpHandler pour refléter simplement la réponse envoyée et s’assurer qu’elle retourne ce que j’attends.

    [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

Il s'agit de HttpContextAccessor pour fixer le chemin à /testpath et vérifie ensuite que le UmamiClient envoie ce correctement.

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

Cela est important pour notre code client Umami car la plupart des données envoyées à partir de chaque demande sont en fait générées dynamiquement par HttpContext object. Nous ne pouvons donc envoyer rien dans un await umamiClient.TrackPageView(); et il envoiera toujours les données correctes en extrayant l'Url de la HttpContext.

Comme nous le verrons plus loin, il est également important que l’Awe envoie des articles comme les UserAgent et IPAddress car ces données sont utilisées par le serveur Umami pour suivre les données et 'trackM SK1 vues des utilisateurs sans utiliser des cookies.

Pour que ce soit prévisible, nous définissons un certain nombre de Consts dans le Consts class. Nous pouvons donc tester les réponses et les demandes prévisibles

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

Autres essais

Ce n’est que le début de notre stratégie d’essai pour Umami.Net, nous devons encore tester IHostedService et de tester les données réelles Umami génére (qui n’est pas documenté ailleurs, mais contient un token JWT avec quelques données utilesM SK2

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

Par conséquent, nous voulons ' tester ce symbole de simulation et éventuellement retourner les données sur chaque visite. uuid(websiteId,ipaddress, useragent)).

En conclusion

Ce n’est que le début de la mise à l’essai du paquet Umami.

Finding related posts...
logo

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