Sto costruendo una piccola API Web utilizzando .NET Core. Voglio praticare TDD e questo è il mio primo tentativo di TDD. Questi sono alcuni casi d'uso dell'API:
- Gli utenti nel ruolo di amministratore possono creare / modificare / eliminare record di lezioni comuni (condivisi).
- Gli utenti nel ruolo dell'insegnante possono creare record di lezione.
- Gli utenti nel ruolo dell'insegnante possono modificare o eliminare i record delle lezioni creati da loro.
- Le lezioni devono avere un nome (non può essere vuoto).
Quindi ora ho qualcosa di simile
using FluentValidation;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
[Route("/lessons")]
[Authorize(Roles = "teacher, admin")]
public class LessonsController : ApiControllerBase
{
private readonly IMediator _mediator;
public LessonsController(IMediator mediator)
{
_mediator = mediator;
}
[HttpPost]
[Authorize(Roles = "admin")]
[Route("create-common-lesson")]
public async Task<IActionResult> CreateCommonLesson([FromBody]CreateLessonCommand command)
{
command.UserId = null;
return await ExecuteRequest(_mediator, command);
}
[HttpPost]
[Route("create-lesson")]
public async Task<IActionResult> CreateLesson([FromBody]CreateLessonCommand command)
{
command.UserId = User.FindFirst("sub")?.Value;
return await ExecuteRequest(_mediator, command);
}
[HttpGet]
[Route("common-lessons")]
public async Task<IActionResult> GetCommonLessons()
{
var query = new LessonQuery();
return Ok(await _mediator.Send(query));
}
}
ed ecco alcuni dei miei test;
using Common;
using FluentAssertions;
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using Xunit;
[Collection("Api Tests")]
public class LessonApiTests : IntegrationTestBase, IClassFixture<WebApiTestFixture>
{
public LessonApiTests(WebApiTestFixture fixture)
: base(fixture)
{
}
[Fact]
public async Task LessonApi_Get_Should_ReturnUnauthorized_When_TokenNotProvided()
{
Client.DefaultRequestHeaders.Authorization = null;
var response = await Client.GetAsync("/lessons/common-lessons");
response.StatusCode.Should().Be(HttpStatusCode.Unauthorized);
}
[Fact]
public async Task LessonApi_GetCommonLessons_ReturnsCommonLesson_WhenTeacherRequests()
{
var response = await TeacherGetAsync("/lessons/common-lessons");
response.StatusCode.Should().Be(HttpStatusCode.OK);
var lessons = await response.Content.ReadAsAsync<IEnumerable<Lesson>>();
lessons.Should().NotBeNullOrEmpty();
}
[Fact]
public async Task LessonApi_CreateCommonLesson_ReturnsForbidden_When_UserIsATeacher()
{
var model = new CreateLessonCommand()
{
Name = "Some-Random-Name"
};
var content = new StringContent(
JsonConvert.SerializeObject(model),
Encoding.UTF8,
"application/json");
var response = await TeacherPostAsync("/lessons/create-common-lesson", content);
response.StatusCode.Should().Be(HttpStatusCode.Forbidden);
}
[Fact]
public async Task LessonApi_CreateCommonLesson_Creates_When_UserIsAnAdminAndValidModelPosted()
{
var model = new CreateLessonCommand()
{
Name = "Some-Random-Name"
};
var content = new StringContent(
JsonConvert.SerializeObject(model),
Encoding.UTF8,
"application/json");
var response = await AdminPostAsync("/lessons/create-common-lesson", content);
response.StatusCode.Should().Be(HttpStatusCode.OK);
response = await TeacherGetAsync("/lessons/common-lessons");
var lessons = await response.Content.ReadAsAsync<IEnumerable<Lesson>>();
lessons.Should().Contain(l => l.Name == "Some-Random-Name");
}
[Fact]
public async Task LessonApi_CreateCommonLesson_ReturnsBadRequest_WhenEmptyNamePosted()
{
var model = new CreateLessonCommand();
var content = new StringContent(
JsonConvert.SerializeObject(model),
Encoding.UTF8,
"application/json");
var response = await AdminPostAsync("/lessons/create-common-lesson", content);
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
response.Content.ReadAsStringAsync().Result.Should().Contain(Constants.ErrorCodes.Lesson.NameCannotBeEmpty);
}
[Fact]
public async Task LessonApi_CreateLesson_Creates_WhenValidModelProvided()
{
}
}
Sto cercando di scrivere test per ogni scenario di utilizzo. Tuttavia, mi sento come se fossi fuori pista qui.
Le mie domande sono:
I test di scrittura per ogni caso di utilizzo sono buone pratiche o cattive pratiche?
Il nome del test di integrazione dovrebbe spiegare il caso d'uso o dovrebbe spiegare come si comporterà l'API? Ad esempio, dovrebbe essere qualcosa di simile:
LessonApi_CreateCommonLesson_Returns_BadRequest_When_EmptyNamePosted
o in questo modo:
Admin_CannotCreateCommonLesson_When_NameIsEmpty