La lliçó anterior va ensamblar l'API completa de BiblioTech sobre el domini, la persistència i la injecció de dependències ja construïts. Abans de desplegar res (Lliçó 5), cal verificar que aquesta API funciona de veritat de cap a cap, no només que cada peça solta passava les seves proves unitàries per separat. Aquesta lliçó amplia la bateria de proves del Mòdul 8 amb proves d'integració sobre els endpoints, repassa les tècniques de depuració més útils a Visual Studio i VS Code, introdueix el registre d'esdeveniments (logging) amb ILogger per diagnosticar problemes ja en producció, i tanca amb un checklist de qualitat concret que ha de superar-se abans d'avançar a la Lliçó 5.

Contingut

  1. Proves unitàries enfront de proves d'integració
  2. Proves d'integració amb WebApplicationFactory
  3. Depuració a Visual Studio i VS Code: breakpoints i watch
  4. Depuració de codi asíncron
  5. Logging bàsic amb ILogger
  6. Checklist de qualitat abans de desplegar

  1. Proves unitàries enfront de proves d'integració

Les proves unitàries del Mòdul 8 (Lliçó 4) verificaven una unitat aïllada de codi —un mètode de Prestec, o Biblioteca.PrestarLlibreAsync amb un mock d'IRepositoriBiblioteca— sense necessitar res extern. Una prova d'integració, en canvi, verifica que diverses peces reals treballant juntes produeixen el resultat esperat: en el cas de BiblioTech, que una petició HTTP real, a través de l'enrutament real d'ASP.NET Core, amb el contenidor de DI real resolent les dependències reals, produeix la resposta correcta.

Prova unitària (Mòdul 8) Prova d'integració (aquesta lliçó)
Què verifica Un mètode aïllat Diversos components reals treballant junts
Dependències Substituïdes per mocks (IRepositoriBiblioteca amb Moq) Reals (o una versió de prova controlada de la base de dades)
Velocitat Mil·lisegons Més lenta (arrenca part de l'aplicació real)
Què detecta Errors de lògica dins d'una unitat Errors de "cablejat": rutes mal registrades, DI mal configurada, serialització JSON incorrecta

Cap substitueix l'altra: les proves unitàries continuen sent la base més ràpida i nombrosa (Mòdul 8), i les proves d'integració afegeixen una capa de confiança diferent, més propera a com un client real farà servir l'API.

  1. Proves d'integració amb WebApplicationFactory

ASP.NET Core ofereix WebApplicationFactory<TEntryPoint>, una classe pensada per arrencar l'aplicació completa en memòria, sense necessitar un port de xarxa real ni un desplegament, i obtenir un HttpClient (el mateix tipus ja usat al Mòdul 5) que dirigeix les seves peticions directament a aquesta aplicació en memòria:

dotnet add BiblioTech.Proves package Microsoft.AspNetCore.Mvc.Testing
using Microsoft.AspNetCore.Mvc.Testing;

public class ApiProvesIntegracio : IClassFixture<WebApplicationFactory<Program>>
{
    private readonly HttpClient _client;

    public ApiProvesIntegracio(WebApplicationFactory<Program> fabrica)
    {
        _client = fabrica.CreateClient(); // HttpClient apuntant a l'app en memoria
    }

    [Fact]
    public async Task GetLlibres_RetornaCodi200()
    {
        // Act
        HttpResponseMessage resposta = await _client.GetAsync("/libros");

        // Assert
        resposta.EnsureSuccessStatusCode(); // llanca si el codi no es 2xx
    }

    [Fact]
    public async Task PostPrestecs_AmbLlibreInexistent_Retorna400()
    {
        // Arrange
        PeticioPrestec peticio = new PeticioPrestec("000-00-000-0000-0", 1);

        // Act
        HttpResponseMessage resposta = await _client.PostAsJsonAsync("/prestamos", peticio);

        // Assert
        Assert.Equal(HttpStatusCode.BadRequest, resposta.StatusCode);
    }
}

IClassFixture<WebApplicationFactory<Program>> li diu a xUnit que comparteixi una única instància de l'aplicació en memòria entre totes les proves d'aquesta classe, en comptes d'arrencar-la de nou a cada mètode (molt més ràpid). PostAsJsonAsync serialitza peticio a JSON automàticament amb System.Text.Json (Mòdul 5), exactament igual que faria un client real contra l'API desplegada. Aquestes proves complementen, sense substituir, les proves unitàries de Moq de 08-04: verifiquen que l'endpoint HTTP realment enruta, deserialitza i respon com s'espera, quelcom que un mock d'IRepositoriBiblioteca no pot comprovar per si sol.

  1. Depuració a Visual Studio i VS Code: breakpoints i watch

Quan una prova falla o el comportament observat no coincideix amb l'esperat, la depuració permet aturar l'execució en un punt concret i inspeccionar l'estat del programa en aquell instant, en comptes d'endevinar què passa a partir de missatges de consola:

Eina Què fa Com es fa servir
Breakpoint (punt d'interrupció) Atura l'execució just abans d'executar una línia concreta Clic al marge esquerre de l'editor, al costat del número de línia
Finestra Watch (inspecció) Mostra el valor actual d'una variable o expressió mentre el programa està aturat Escriure l'expressió (prestec.DataDevolucio, cataleg.Count) al panell de Watch
Step Over / Step Into / Step Out Avança l'execució línia a línia, entrant o no en les crides a altres mètodes Tecles de funció (F10/F11/Majús+F11 a Visual Studio; equivalents a VS Code)
Pila de crides (Call Stack) Mostra la cadena completa de mètodes que va portar fins al punt actual Panell dedicat, visible automàticament en aturar-se en un breakpoint

Un flux típic de depuració sobre POST /prestamos/{id}/devolucion (09-03, apartat 6): col·locar un breakpoint a la línia prestec.RegistrarDevolucio();, llançar l'API en mode depuració (F5 a Visual Studio o VS Code), fer la petició real amb Swagger o curl, i quan l'execució s'aturi, inspeccionar al Watch el valor de prestec.DataPrestec i politica.CalcularSancio(...) abans que s'executin, per confirmar que les dades que arriben són les esperades.

  1. Depuració de codi asíncron

Depurar mètodes async/await (Mòdul 4) té una particularitat: en fer Step Into sobre un await, l'execució pot "saltar" de forma que sembli que el fil canvia, perquè efectivament pot reprendre's en un fil diferent després de l'espera (Mòdul 6, multifil). Algunes pràctiques ajuden perquè això no confongui:

  • Col·locar el breakpoint després de l'await (per exemple, just on es fa servir el resultat d'ObtenirMetadadesPerIsbnAsync) per inspeccionar el valor ja resolt, en comptes d'intentar seguir l'espera mateixa pas a pas.
  • Revisar la pila de crides amb atenció després d'un await: pot mostrar-se més curta del que s'esperava, perquè part de la pila "anterior" a l'espera ja no existeix de la mateixa forma un cop el mètode es reprèn.
  • Per al Task.Delay(1000) de PrestarLlibreAsync (08-04), un breakpoint just després d'aquesta línia permet confirmar que l'execució efectivament va esperar, sense necessitat de depurar l'espera en si mateixa pas a pas.

  1. Logging bàsic amb ILogger

Els breakpoints són l'eina adequada mentre es desenvolupa i es pot reproduir el problema en local. En producció (Lliçó 5), no hi ha manera d'aturar l'execució amb un breakpoint: l'eina equivalent és el logging, missatges que el programa escriu de forma contínua descrivint el que va fent, per poder-los revisar després si alguna cosa falla. ASP.NET Core injecta automàticament un ILogger<T> a qualsevol classe que el declari per constructor (08-03, injecció per constructor):

app.MapPost("/prestamos/{id}/devolucion", (
    int id, Biblioteca biblioteca, IPoliticaSancio politica, ILogger<Program> logger) =>
{
    Prestec? prestec = biblioteca.Prestecs.ElementAtOrDefault(id);

    if (prestec is null)
    {
        logger.LogWarning("Intent de devolucio d'un prestec inexistent: {Id}", id);
        return Results.NotFound($"No existeix el prestec #{id}.");
    }

    prestec.RegistrarDevolucio();
    decimal sancio = prestec.CalcularSancio(politica);

    logger.LogInformation(
        "Devolucio registrada per al prestec {Id}. Sancio aplicada: {Sancio:C}", id, sancio);

    biblioteca.GuardarCataleg();
    return Results.Ok(new { prestec.DataDevolucio, Sancio = sancio });
});
Nivell Quan fer-lo servir
LogInformation Successos normals que interessa poder rastrejar després (un préstec es va registrar, una devolució es va processar)
LogWarning Quelcom inesperat però no greu: una petició amb dades que no es van trobar, un reintent
LogError Una fallada real que va impedir completar l'operació (una excepció capturada, un servei extern caigut)

Els marcadors {Id} i {Sancio:C} al missatge no són interpolació de cadenes de C# ($"..."): són paràmetres amb nom que el proveïdor de logging estructura per separat del text, cosa que permet després cercar o filtrar registres per Id sense haver d'analitzar el text del missatge. Per defecte, ASP.NET Core escriu aquests missatges a la consola durant el desenvolupament; en producció (Lliçó 5) es configuren proveïdors addicionals (fitxers, serveis de monitorització) sense canviar ni una línia de les crides a logger.LogInformation(...).

  1. Checklist de qualitat abans de desplegar

Abans de passar a la Lliçó 5 (Desplegament), aquest checklist reuneix el que s'ha verificat en aquesta lliçó amb el que ja es va veure a 08-05:

  • [ ] Totes les proves unitàries del Mòdul 8 (Prestec, Biblioteca.PrestarLlibreAsync) estan en verd.
  • [ ] Les noves proves d'integració de l'apartat 2 sobre els endpoints principals estan en verd.
  • [ ] L'analitzador de codi del projecte no reporta advertències sense justificar (dotnet build sense warnings, o cadascuna revisada explícitament).
  • [ ] El checklist de revisió de 08-05 (nomenclatura, dependències injectades, nul·labilitat consistent) s'ha aplicat sobre el codi nou de 09-03.
  • [ ] Els endpoints que poden fallar per causes esperades (préstec no disponible, soci no trobat, metadades externes no disponibles) retornen un codi HTTP i un missatge clars, no un error 500 genèric.
  • [ ] S'ha afegit logging (ILogger) almenys als punts on quelcom pot fallar per causes externes (persistència, consulta de metadades).

Superar aquest checklist no és una formalitat burocràtica: és la confirmació concreta que l'API està a punt per al pas següent, molt més arriscat si faltés alguna d'aquestes coses: publicar-la fora de l'entorn de desenvolupament.

Errors Comuns i Consells

  • Confondre una prova d'integració amb una prova manual: una prova d'integració ha de poder executar-se automàticament amb dotnet test, igual que una prova unitària; provar l'API a mà amb Swagger és útil durant el desenvolupament, però no substitueix una prova automatitzada que es repeteixi a cada canvi.
  • Fer servir interpolació de cadenes ($"...") a crides a ILogger: perd l'estructura dels paràmetres amb nom (apartat 5), dificultant cercar o filtrar registres després; fes servir sempre els marcadors {NomParametre} amb els valors com a arguments addicionals.
  • Depurar en producció amb breakpoints: no és una opció real (no hi ha manera d'"aturar" un servidor en producció sense interrompre els usuaris); el logging de l'apartat 5 és l'eina pensada exactament per a aquest escenari.
  • Consell: si una prova d'integració falla de forma intermitent (a vegades passa, a vegades no), sospita primer de dependències de temps real (com el Task.Delay de PrestarLlibreAsync) o d'estat compartit entre proves que no es neteja correctament entre una i una altra.

Exercicis

  1. Escriu una prova d'integració PostSocios_AmbSociValid_Retorna201 que faci un POST a /socios amb un Soci vàlid i comprovi que la resposta té el codi 201 Created.

  2. Afegeix una crida a logger.LogError al catch (InvalidOperationException ex) de l'endpoint POST /prestamos de 09-03, registrant el missatge de l'excepció abans de retornar Results.Conflict(ex.Message).

Solucions

[Fact]
public async Task PostSocios_AmbSociValid_Retorna201()
{
    // Arrange
    Soci soci = new Soci(99, "Marta Ruiz");

    // Act
    HttpResponseMessage resposta = await _client.PostAsJsonAsync("/socios", soci);

    // Assert
    Assert.Equal(HttpStatusCode.Created, resposta.StatusCode);
}
try
{
    await biblioteca.PrestarLlibreAsync(llibre, soci);
}
catch (InvalidOperationException ex)
{
    logger.LogError(ex, "Fallada en registrar el prestec per a l'ISBN {Isbn}", peticio.Isbn);
    return Results.Conflict(ex.Message);
}

(Nota: aquesta versió requereix afegir ILogger<Program> logger com a paràmetre addicional de l'endpoint, igual que a l'exemple de l'apartat 5; logger.LogError(ex, ...) registra a més l'excepció completa, no només el missatge.)

Conclusió

Aquesta lliçó ha ampliat la confiança en BiblioTech més enllà de les proves unitàries del Mòdul 8: ara també existeixen proves d'integració amb WebApplicationFactory que verifiquen els endpoints reals de cap a cap, s'han repassat les tècniques de depuració —inclosa la particularitat de depurar codi asíncron—, s'ha afegit logging amb ILogger per diagnosticar problemes un cop desplegada l'aplicació, i s'ha fixat un checklist de qualitat concret. Amb tots aquests punts superats, BiblioTech està a punt, per fi, per al pas que tanca el projecte i el curs sencer: la lliçó final, Desplegament, publicarà aquesta API, la contenidoritzarà amb Docker, i configurarà el seu comportament per entorn per a producció.

Curs de Programació en C#

Mòdul 1: Introducció al C#

Mòdul 2: Estructures de Control

Mòdul 3: Programació Orientada a Objectes

Mòdul 4: Conceptes Avançats de C#

Mòdul 5: Treballant amb Dades

Mòdul 6: Temes Avançats

Mòdul 7: Construcció d'Aplicacions

Mòdul 8: Bones Pràctiques i Patrons de Disseny

Mòdul 9: Projecte Final

© Copyright 2026. Tots els drets reservats