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
- Proves unitàries enfront de proves d'integració
- Proves d'integració amb
WebApplicationFactory - Depuració a Visual Studio i VS Code: breakpoints i watch
- Depuració de codi asíncron
- Logging bàsic amb
ILogger - Checklist de qualitat abans de desplegar
- 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.
- Proves d'integració amb
WebApplicationFactory
WebApplicationFactoryASP.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:
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.
- 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.
- 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)dePrestarLlibreAsync(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.
- Logging bàsic amb
ILogger
ILoggerEls 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(...).
- 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 buildsense 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 aILogger: 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.DelaydePrestarLlibreAsync) o d'estat compartit entre proves que no es neteja correctament entre una i una altra.
Exercicis
-
Escriu una prova d'integració
PostSocios_AmbSociValid_Retorna201que faci unPOSTa/sociosamb unSocivàlid i comprovi que la resposta té el codi201 Created. -
Afegeix una crida a
logger.LogErroralcatch (InvalidOperationException ex)de l'endpointPOST /prestamosde 09-03, registrant el missatge de l'excepció abans de retornarResults.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#
- Introducció al C#
- Configuració de l'Entorn de Desenvolupament
- Programa Hola Món
- Sintaxi i Estructura Bàsica
- Variables i Tipus de Dades
- Arrays i Cadenes de Text
Mòdul 2: Estructures de Control
Mòdul 3: Programació Orientada a Objectes
- Classes i Objectes
- Mètodes
- Constructors i Destructors
- Herència
- Polimorfisme
- Encapsulació
- Abstracció
- Structs i Records: Tipus per Valor i per Referència
Mòdul 4: Conceptes Avançats de C#
- Interfícies
- Delegats i Esdeveniments
- Pattern Matching i Característiques Modernes de C#
- Genèrics
- Col·leccions
- LINQ (Consulta Integrada en el Llenguatge)
- Programació Asíncrona
Mòdul 5: Treballant amb Dades
- Entrada/Sortida de Fitxers
- Serialització
- Connectivitat amb Bases de Dades
- Entity Framework
- Treball amb JSON i Consum d'APIs REST
Mòdul 6: Temes Avançats
- Reflexió
- Atributs
- Programació Dinàmica
- Gestió de Memòria i Recollida d'Escombraries
- Multifil i Programació Paral·lela
Mòdul 7: Construcció d'Aplicacions
Mòdul 8: Bones Pràctiques i Patrons de Disseny
- Estàndards de Codificació i Bones Pràctiques
- Patrons de Disseny
- Injecció de Dependències i Inversió de Control
- Proves Unitàries
- Revisió i Refactorització de Codi
