Aller au contenu principal

2-6-2 Test d'une requête POST d'un formulaire

Nous avons des utilisateurs et bientôt nous testerons des requêtes en tant qu'utilisateur connecté, donc le prétexte est bon pour tester la connexion à l'application.

Nous verrons d'un côté comment tester une requête POST envoyant des données de formulaire et le code que nous allons écrire nous sera utile plus loin dans le contexte de tester des requêtes en tant qu'utilisateur authentifié.

Créer un fichier de tests pour l'authentification

  1. Sous Web.Mvc/TestsIntegration -> Add -> Class/Interface
  2. Nommer la classe AuthTests
  3. Faites hériter la classe de WebMvcIntegrationTestBase et créez le constructeur
    Snowfall.Tests/Web.Mvc/TestsIntegration/AuthTests.cs
    public class AuthTests : WebMvcIntegrationTestBase
    {
    public AuthTests(
    SnowfallMvcApplicationFactory application,
    TestDatabaseFixture database) : base(application, database)
    {
    }
    }

Créer une fonction de test pour la connexion

Snowfall.Tests/Web.Mvc/TestsIntegration/AuthTests.cs
[Fact]
public async Task Connexion_AuthentificationSucces_RetourneRedirectionAccueil()
{

}
info
  • action: Connexion
  • scénario: AuthentificationSucces (l'authentification est réussie)
  • résultat attendu: RetourneRedirectionAccueil (redirige vers l'accueil)

Tester la connexion

Allons-y, on peut tester la connexion!

  1. Dans un premier temps, on crée le client HTTP

    Snowfall.Tests/Web.Mvc/TestsIntegration/AuthTests.cs
    [Fact]
    public async Task Connexion_AuthentificationSucces_RetourneRedirectionAccueil()
    {
    // Arrange
    var client = Application.CreateClient(new WebApplicationFactoryClientOptions
    {
    AllowAutoRedirect = false
    });
    }
    info

    Par défaut, le client HTTP suit les redirections. Par exemple, après la connexion, l'utilisateur est redirigé vers la page d'accueil.

    On veut premièrement tester, entre autres, qu'une redirection est retournée suite à la connexion et donc que la connexion semble être un succès. Si on suit automatiquement les redirections, cette information est perdue.

    On crée notre propre client plutôt que celui de la classe de base puisqu'on doit passer une option personnalisée.

  2. Ensuite, on appelle la page de connexion pour obtenir la page de connexion et vérifier que la page contenant le formulaire retourne bien un code de succès (ex.: 200 OK).

    Snowfall.Tests/Web.Mvc/TestsIntegration/AuthTests.cs
    [Fact]
    public async Task Connexion_AuthentificationSucces_RetourneRedirectionAccueil()
    {
    // Arrange
    var client = Application.CreateClient(new WebApplicationFactoryClientOptions
    {
    AllowAutoRedirect = false
    });

    HttpResponseMessage reponse = await client.GetAsync("Auth/Connexion");
    reponse.EnsureSuccessStatusCode();
    }
  3. Puis, on bâtit les informations qui seront envoyées via le formulaire. Ces dernières doivent être construites sous forme de Dictionary, soit sous forme d'objet clé-valeur:

    Snowfall.Tests/Web.Mvc/TestsIntegration/AuthTests.cs
    [Fact]
    public async Task Connexion_AuthentificationSucces_RetourneRedirectionAccueil()
    {
    // Arrange
    var client = Application.CreateClient(new WebApplicationFactoryClientOptions
    {
    AllowAutoRedirect = false
    });

    HttpResponseMessage reponse = await client.GetAsync("Auth/Connexion");
    reponse.EnsureSuccessStatusCode();

    var connexionViewModelDictionary = new Dictionary<string, string>
    {
    { "Email", "u@ser.com" },
    { "Password", "!User122432" }
    };
    }
    info

    On fera une requête vers la route /auth, associée au contrôleur d'authentification, acceptant en paramètre un ViewModel de connexion. C'est pourquoi nous créons un dictionnaire calqué sur le format de ConnexionViewModel.

  4. Pour enfin faire un POST sur l'action du formulaire de connexion avec les données de connexion. On utilise FormUrlEncodedContent, qui prend en entrée un dictionnaire, pour encoder les valeurs.

    Snowfall.Tests/Web.Mvc/TestsIntegration/AuthTests.cs
    [Fact]
    public async Task Connexion_AuthentificationSucces_RetourneRedirectionAccueil()
    {
    // Arrange
    var client = Application.CreateClient(new WebApplicationFactoryClientOptions
    {
    AllowAutoRedirect = false
    });

    HttpResponseMessage reponse = await client.GetAsync("Auth/Connexion");
    reponse.EnsureSuccessStatusCode();

    var connexionViewModelDictionary = new Dictionary<string, string>
    {
    { "Email", "u@ser.com" },
    { "Password", "!User122432" }
    };

    // Act
    reponse = await client.PostAsync(
    "Auth",
    new FormUrlEncodedContent(connexionViewModelDictionary));
    }
  5. Finalement, on teste que le retour est bien une redirection et que la redirection est bien effectuée vers l'accueil.

    Snowfall.Tests/Web.Mvc/TestsIntegration/AuthTests.cs
    [Fact]
    public async Task Connexion_AuthentificationSucces_RetourneRedirectionAccueil()
    {
    // Arrange
    var client = Application.CreateClient(new WebApplicationFactoryClientOptions
    {
    AllowAutoRedirect = false
    });

    HttpResponseMessage reponse = await client.GetAsync("Auth/Connexion");
    reponse.EnsureSuccessStatusCode();

    var connexionViewModelDictionary = new Dictionary<string, string>
    {
    { "Email", "u@ser.com" },
    { "Password", "!User122432" }
    };

    // Act
    reponse = await client.PostAsync(
    "Auth",
    new FormUrlEncodedContent(connexionViewModelDictionary));

    // Assert
    Assert.Equal(HttpStatusCode.Redirect, reponse.StatusCode);
    Assert.Equal("/",reponse.Headers.Location?.OriginalString);
    }

Exécuter le test

Si vous exécutez le test, vous obtiendrez un échec du test:

Assert.Equal() Failure
Expected: Redirect
Actual: BadRequest

Plutôt qu'une redirection, on obtient une réponse BadRequest 🤔.

Il nous manque le jeton CSRF qui valide la provenance de la requête!

Avant d'envoyer le formulaire, il nous faudra extraire le jeton CSRF de ce dernier et l'envoyer avec la requête.

Obtenir le jeton CSRF

Installer AngleSharp

Pour obtenir le jeton, on il est possible d'utiliser une librairie qui facilite la lecture du HTML reçu. La librairie que nous utiliserons est AngleSharp.

  1. Sous le projet Snowfall.Tests -> Manage NuGet Packages
  2. Faire une recherche pour AngleSharp
  3. Installer la librairie dans le projet

Ajouter la classe utilitaire HtmlHelper

Microsoft propose une classe utilitaire pour lire une réponse HTTP et la convertir en document IHtmlDocument AngleSharp (réf: https://github.com/dotnet/AspNetCore.Docs.Samples/blob/main/test/integration-tests/IntegrationTestsSample/tests/RazorPagesProject.Tests/Helpers/HtmlHelpers.cs). Réutilisons cette classe.

  1. Sous le projet Snowfall.Tests, créez un dossier Helpers
  2. Sous le dossier Helpers du projet Snowfall.Tests -> Add -> Class/Interface
  3. Nommez la classe HtmlHelpers et ajoutez-y le code suivant:
    Snowfall.Tests/Helpers/HtmlHelpers.cs
    public class HtmlHelpers
    {
    public static async Task<IHtmlDocument> GetDocumentAsync(HttpResponseMessage response)
    {
    var content = await response.Content.ReadAsStringAsync();
    var document = await BrowsingContext.New()
    .OpenAsync(ResponseFactory, CancellationToken.None);
    return (IHtmlDocument)document;

    void ResponseFactory(VirtualResponse htmlResponse)
    {
    htmlResponse
    .Address(response.RequestMessage.RequestUri)
    .Status(response.StatusCode);

    MapHeaders(response.Headers);
    MapHeaders(response.Content.Headers);

    htmlResponse.Content(content);

    void MapHeaders(HttpHeaders headers)
    {
    foreach (var header in headers)
    {
    foreach (var value in header.Value)
    {
    htmlResponse.Header(header.Key, value);
    }
    }
    }
    }
    }
    }

Récupérer le jeton de la réponse

On peut finalement récupérer le jeton et l'inclure dans le dictionnaire envoyé en utilisant la nouvelle classe utilitaire HtmlHelpers. Le principe ici est que le jeton CSRF est toujours nommé __RequestVerificationToken dans un formulaire .NET MVC. On cherche donc dans la page une balise input de ce nom et on en extrait la valeur.

Snowfall.Tests/Web.Mvc/TestsIntegration/AuthTests.cs
[Fact]
public async Task Connexion_AuthentificationSucces_RetourneRedirectionAccueil()
{
// Arrange
var client = Application.CreateClient(new WebApplicationFactoryClientOptions
{
AllowAutoRedirect = false
});

HttpResponseMessage reponse = await client.GetAsync("Auth/Connexion");
reponse.EnsureSuccessStatusCode();

var document = await HtmlHelpers.GetDocumentAsync(reponse);
var csrfInput = document.QuerySelector("input[name='__RequestVerificationToken']") as IHtmlInputElement;
string? csrf = csrfInput?.Value;

var connexionViewModelDictionary = new Dictionary<string, string>
{
{ "Email", "u@ser.com" },
{ "Password", "!User122432" },
{ "__RequestVerificationToken", csrf }
};

// Act
reponse = await client.PostAsync(
"Auth",
new FormUrlEncodedContent(connexionViewModelDictionary));

// Assert
Assert.Equal(HttpStatusCode.Redirect, reponse.StatusCode);
Assert.Equal("/",reponse.Headers.Location?.OriginalString);
}

Test

Vous pouvez tester et le test devrait finalement passer!

Pour être certain hors de tout doute que l'utilisateur est bien connecté, on peut charger la page d'accueil à la fin du test et vérifier que le nom de l'utilisateur est bien dans la page.

Snowfall.Tests/Web.Mvc/TestsIntegration/AuthTests.cs
    //...

// Assert
Assert.Equal(HttpStatusCode.Redirect, reponse.StatusCode);
Assert.Equal("/",reponse.Headers.Location?.OriginalString);

reponse = await client.GetAsync(reponse.Headers.Location.ToString());
Assert.Contains("Bonjour, Uti", await reponse.Content.ReadAsStringAsync());
}