Tag: testing, unit-test, integration-test, e2e, jest, playwright, tdd
Livello: intermedio
TL;DR: Strategie, piramide dei test e pattern di automazione per garantire affidabilita' del codice e prevenire regressioni in produzione.
Piramide dei test: base ampia di unit test, livello intermedio di integrazione, vertice contenuto di E2E.
Unit test: verifica componenti o funzioni pure in isolamento, con mock delle dipendenze esterne.
Test di integrazione: validazione dell'interazione reale tra moduli, database, middleware e API interne.
Test End-to-End: simulazione dei flussi utente completi su interfaccia reale o browser headless.
TDD (Test-Driven Development): ciclo Red-Green-Refactor basato su specifica dei requisiti prima dell'implementazione.
Code coverage: metrica quantitativa di linee, rami e funzioni eseguite durante la suite di test.
Test doppi: distinzione operativa tra dummy, stub, spy, mock e fake object.
Regression testing: esecuzione sistematica della suite a ogni pipeline CI per prevenire guasti pregressi.
Smoke test: controlli minimi di sanita' per verificare l'avvio e la stabilita' post-deploy del sistema.
Flaky test: test non deterministici causati da race condition, timing errati o dipendenze condivise.
Q: Come approcci la piramide dei test in un'applicazione moderna?R: Concentro la maggior parte degli sforzi su unit test veloci e test di integrazione affidabili. Mantengo i test E2E focalizzati esclusivamente sui flussi di business critici per evitare lentezza ed instabilita' nella pipeline CI/CD.
Q: Quando preferisci un mock rispetto a un database o servizio reale?R: Uso mock negli unit test per isolare la logica pura e simulare errori rari o timeout di rete. Nei test di integrazione preferisco sempre database effettivi su container effimeri per testare query, vincoli e transazioni reali.
Q: Come gestisci e riduci i flaky test in CI?R: Rimuovo attese arbitrarie usando polling condizionale su stato ed eventi del DOM o backend. Isolo lo stato dei dati tra ogni test per eliminare contaminazioni e dipendenze sequenziali non deterministiche.
Testare dettagli implementativi privati invece del comportamento e dell'interfaccia pubblica.
Presenza di race condition causate dall'uso di sleep fisso anziche' attese esplicite di eventi.
Dipendenza dall'ordine di esecuzione dei test dovuta a stato globale condiviso o database non ripulito.
Mocking eccessivo che trasforma i test in specchi del codice senza validare scenari reali.
Inseguire il 100% di coverage ignorando la qualita' effettiva delle asserzioni sui casi limite.
// Pattern AAA (Arrange, Act, Assert) con isolamento dipendenze
describe('UserService.register', () => {
it('should throw error when user already exists', async () => {
const userRepositoryMock = {
findByEmail: jest.fn().mockResolvedValue({ id: '1', email: 'test@example.com' }),
save: jest.fn(),
};
const sut = new UserService(userRepositoryMock);
await expect(sut.register('test@example.com', 'pwd')).rejects.toThrow(UserExistsException);
expect(userRepositoryMock.save).not.toHaveBeenCalled();
});
});
Bash
# Esecuzione mirata con coverage in Jest
npm test -- --coverage --runInBand --detectOpenHandles
[ ] Pattern Arrange-Act-Assert e Clean Architecture nei test
[ ] Strategie di mock parziale con SpyOn e factory functions
[ ] Configurazione di database transazionali per test di integrazione
[ ] Attese esplicite e selettori accessibili (byRole) in Playwright e Testing Library
[ ] Rilevamento e quarantena dei flaky test nei workflow GitHub Actions