Che cosa sono le convenzioni di denominazione dei test TDD e di integrazione?

1

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
    
posta NazmiAltun 30.01.2018 - 17:51
fonte

1 risposta

3

Una convenzione di denominazione comune per i test (unità, AAT e integrazione) è "Given-When-Then" (o GWT). Ogni parte corrisponderà approssimativamente a una delle tre A - Arrange è "Given", Act è "When" e Assert è "Then". È normale prefflicare il nome del test per la ricercabilità, magari con l'oggetto specifico da testare (specialmente nei test unitari), ma potrebbe anche trattarsi di un'area generale del software.

Applicando questo al tuo esempio, questo produrrebbe qualcosa come LessonApi_GivenCreatingCommonLesson_WhenEmptyNamePosted_ThenBadRequestIsReturned .

Ricorda che le suite di test sono spesso utilizzate come punto di scavatura per le persone che sono nuove nella tua base di codice, quindi la ricerca è davvero importante; a tal fine, utilizzando un nome che include parole che puoi facilmente vedere, la ricerca di qualcuno può essere utile per i futuri lettori del tuo codice (tra cui "tu, in cinque anni").

    
risposta data 31.01.2018 - 15:55
fonte

Leggi altre domande sui tag