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

# Introduction

Maintenant, j’ai le [Umami.Paquet Net](https://www.nuget.org/packages/Umami.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.

[TOC]

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

# É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`.

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

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.

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

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

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

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.

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

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.

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

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

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

```

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

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

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

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

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.