2-7-3 Faire une requête authentifiée
Pour tester une requête nécessitant l'autorisation à l'API, essayons de créer un événement via l'API.
-
Créons la fonction de test
Creer_EvenementValide_RetourneOk()
dans la classe de test des événementsEvenementsTest
.Snowfall.Tests/Web.Api/TestsIntegration/EvenementsTests.cs//...
[Fact]
public async Task Creer_EvenementValide_RetourneOk()
{
} -
Ensuite, préparons les données en créant un DTO et en créant le contexte d'utilisateur connecté via
CreerClientUtilisateur
.Snowfall.Tests/Web.Api/TestsIntegration/EvenementsTests.cs[Fact]
public async Task Creer_EvenementValide_RetourneOk()
{
// Arrange
string url = "api/evenements";
string courriel = "a@dmin.com";
var client = await CreerClientUtilisateur(courriel);
var ville = new Ville()
{
Nom = "Paris",
PaysIso = "fr"
};
ville = await VilleRepository.Create(ville);
var creerEvenementDto = new CreerEvenementDto
{
Nom = "Nouvel événement",
Description = "Description",
Date = DateTime.Now,
VilleId = ville.Id,
Capacite = 300,
Prix = new Decimal(49.88)
};
}infoRemarquez que nous commençons à remarquer une certaine duplication pour l'insertion d'une ville. Il pourrait être pertinent de sortir dans une fonction de la classe du test.
infoIci, je sais que
a@dmin.com
est un utilisateur possédant le rôleADMIN
puisque dans les seed de BD j'ai fait l'association entre l'utilisateur et le rôle via la table pivot. C'est la seule portion que nous conservons des seeds, sans réinitialiser la BD à chaque occasion -
Puis, on effectue la requête à l'aide de
PostAsJsonAsync
pour ensuite vérifier que le retour est un succès.Snowfall.Tests/Web.Api/TestsIntegration/EvenementsTests.cspublic async Task Creer_EvenementValide_RetourneOk()
{
// Arrange
string url = "api/evenements";
string courriel = "a@dmin.com";
var client = await CreerClientUtilisateur(courriel);
var ville = new Ville()
{
Nom = "Paris",
PaysIso = "fr"
};
ville = await VilleRepository.Create(ville);
var creerEvenementDto = new CreerEvenementDto
{
Nom = "Nouvel événement",
Description = "Description",
Date = DateTime.Now,
VilleId = ville.Id,
Capacite = 300,
Prix = new Decimal(49.88)
};
// Act
var reponse = await client.PostAsJsonAsync(url, creerEvenementDto);
// Assert
Assert.Equal(HttpStatusCode.OK, reponse.StatusCode);
} -
On peut aussi valider qu'un identifiant a bien été assigné et qu'un DTO d'événement est retourné.
Snowfall.Tests/Web.Api/TestsIntegration/EvenementsTests.cs[Fact]
public async Task Creer_EvenementValide_RetourneOk()
{
// Arrange
string url = "api/evenements";
string courriel = "a@dmin.com";
var client = await CreerClientUtilisateur(courriel);
var ville = new Ville()
{
Nom = "Paris",
PaysIso = "fr"
};
ville = await VilleRepository.Create(ville);
var creerEvenementDto = new CreerEvenementDto
{
Nom = "Nouvel événement",
Description = "Description",
Date = DateTime.Now,
VilleId = ville.Id,
Capacite = 300,
Prix = new Decimal(49.88)
};
// Act
var reponse = await client.PostAsJsonAsync(url, creerEvenementDto);
// Assert
Assert.Equal(HttpStatusCode.OK, reponse.StatusCode);
var evenementDto = await reponse.Content.ReadFromJsonAsync<EvenementDto>();;
Assert.NotNull(evenementDto);
Assert.True(evenementDto.Id > 0);
Assert.Equal(creerEvenementDto.Nom, evenementDto.Nom);
} -
Finalement, une approche plus simple et plutôt efficace que j'affectionne particulièrement est de vérifier le nombre d'items dans la BD avant l'insertion et après l'insertion. Ainsi, si l'insertion a réussi, la quantité d'items dans la BD aura augmenté de 1. On s'assure ainsi que le contrôleur n'a pas juste retourné un DTO sans faire la sauvegarde en BD.
Snowfall.Tests/Web.Api/TestsIntegration/EvenementsTests.cs[Fact]
public async Task Creer_EvenementValide_RetourneOk()
{
// Arrange
string url = "api/evenements";
string courriel = "a@dmin.com";
var client = await CreerClientUtilisateur(courriel);
int compteInitial = (await EvenementRepository.GetAll()).Count;
var ville = new Ville()
{
Nom = "Paris",
PaysIso = "fr"
};
ville = await VilleRepository.Create(ville);
var creerEvenementDto = new CreerEvenementDto
{
Nom = "Nouvel événement",
Description = "Description",
Date = DateTime.Now,
VilleId = ville.Id,
Capacite = 300,
Prix = new Decimal(49.88)
};
// Act
var reponse = await client.PostAsJsonAsync(url, creerEvenementDto);
// Assert
Assert.Equal(HttpStatusCode.OK, reponse.StatusCode);
var evenementDto = await reponse.Content.ReadFromJsonAsync<EvenementDto>();;
Assert.NotNull(evenementDto);
Assert.True(evenementDto.Id > 0);
Assert.Equal(creerEvenementDto.Nom, evenementDto.Nom);
int compteFinal = (await EvenementRepository.GetAll()).Count;
Assert.Equal(compteInitial+1, compteFinal);
} -
Finalement, on peut alléger le test en déléguant à une méthode privée la création du DTO de test.
Snowfall.Tests/TestsApi/TestsIntegration/EvenementsTests.cspublic class EvenementsTests : IClassFixture<SnowfallApiApplication>
{
//...
[Fact]
public async Task Creer_EvenementValide_RetourneOk()
{
// Arrange
using var scope = _application.Services.CreateScope();
var evenementRepository = scope.ServiceProvider.GetRequiredService<IEvenementRepository>();
int compteInitial = (await evenementRepository.GetAll()).Count;
string url = "api/evenements";
string courriel = "a@dmin.com";
var client = await _application.CreerClientUtilisateur(courriel);
var evenementDto = EvenementDto();
// Act
var reponse = await client.PostAsJsonAsync(url, evenementDto);
Assert.Equal(HttpStatusCode.OK, reponse.StatusCode);
int compteFinal = (await evenementRepository.GetAll()).Count;
Assert.Equal(compteFinal, compteInitial+1);
}
private EvenementDto EvenementDto()
{
return new EvenementDto
{
Nom = "Nouvel événement",
Description = "Description",
Date = DateTime.Now,
VilleId = 1,
Capacite = 300,
Prix = new Decimal(49.88)
};
//highlight-end}
}
Tester un ou des cas invalides
Il est important de tester d'autres cas de figure. Par exemple, que la création n'est pas réussie dans le cas d'un événement non valide.
Par exemple, voici un test similaire, mais adapté pour mettre le nom de l'événement vide et ensuite vérifier que la réponse contient bien des erreurs de validation.
[Fact]
public async Task Creer_EvenementNonValide_RetourneErreursValidation()
{
// Arrange
string url = "api/evenements";
string courriel = "a@dmin.com";
var client = await CreerClientUtilisateur(courriel);
int compteInitial = (await EvenementRepository.GetAll()).Count;
var ville = new Ville()
{
Nom = "Paris",
PaysIso = "fr"
};
ville = await VilleRepository.Create(ville);
var creerEvenementDto = new CreerEvenementDto
{
Nom = "",
Description = "Description",
Date = DateTime.Now,
VilleId = ville.Id,
Capacite = 300,
Prix = new Decimal(49.88)
};
// Act
var reponse = await client.PostAsJsonAsync(url, creerEvenementDto);
// Assert
Assert.Equal(HttpStatusCode.BadRequest, reponse.StatusCode);
var problemDetails = await reponse.Content.ReadFromJsonAsync<ValidationProblemDetails>();
Assert.NotNull(problemDetails);
Assert.Equal("One or more validation errors occurred.", problemDetails.Title);
Assert.NotEmpty(problemDetails.Errors);
Assert.Equal(new[] { "Le nom est requis" }, problemDetails.Errors["Nom"]);
int compteFinal = (await EvenementRepository.GetAll()).Count;
Assert.Equal(compteInitial, compteFinal);
}
Réduire la répétition
Remarquez que dans les tests, on crée le même DTO de base. Afin d'éviter la répétition, vous pouvez créer une fonction dans la classe telle que:
private async Task<CreerEvenementDto> CreerEvenementDto()
{
var ville = new Ville()
{
Nom = "Paris",
PaysIso = "fr"
};
ville = await VilleRepository.Create(ville);
var creerEvenementDto = new CreerEvenementDto
{
Nom = "Nouvel événement",
Description = "Description",
Date = DateTime.Now,
VilleId = ville.Id,
Capacite = 300,
Prix = new Decimal(49.88)
};
return creerEvenementDto;
}
Cette fonction peut ensuite être utilisée dans les tests de sorte à réduire la répétition.
public async Task Creer_EvenementNonValide_RetourneErreursValidation()
{
// Arrange
string url = "api/evenements";
string courriel = "a@dmin.com";
var client = await CreerClientUtilisateur(courriel);
int compteInitial = (await EvenementRepository.GetAll()).Count;
CreerEvenementDto creerEvenementDto = await CreerEvenementDto();
creerEvenementDto.Nom = "";