Stiamo assumendo! Visualizza le posizioni aperte

Test Driven Development (TDD): Vantaggi ed esempi pratici

Introduzione

Nell’ambito dello sviluppo software, il Test Driven Development (TDD) sta emergendo come un fondamentale approccio per creare applicazioni solide e affidabili.
Attraverso la scrittura anticipata dei test, il TDD offre benefici significativi che influenzano la qualità e l’efficienza dello sviluppo.
In questo articolo, esploreremo i vantaggi del Test Driven Development, con esempi concreti in JavaScript, evidenziando come questa metodologia possa notevolmente migliorare il processo di sviluppo.

TDD e ottimizzazione del codice

Consideriamo l’esempio di una classe JavaScript per una calcolatrice. 
Cominciamo scrivendo un test che delinea il comportamento atteso:

				
					describe("Calculator class", () => {
    test('it should add two numbers', () => {
        const calc = new Calculator();
        expect(calc.add(2, 3)).toBe(5);
    });
})

				
			

Il TDD ci impone di ponderare la progettazione del codice prima ancora di iniziare a programmare, risultando in una base solida per lo sviluppo.
Per creare la funzionalità richiesta basta scrivere “il codice necessario per far passare il test”.

				
					export class Calculator {
    add(numberA, numberB) {
        return numberA + numberB;
    }
}
				
			

L’eccessiva semplificazione dell’esempio non lo fa apprezzare, ma scrivere prima il test e poi il codice, specialmente in software reali, è una pratica che nella maggior parte dei casi permette di ridurre notevolmente la mole di linee di codice prodotte, perché lo sviluppatore tende ad avere molto più chiaro il requisito da soddisfare. 

Estenzione delle funzionalità in TDD

Supponiamo di dover estendere la funzione add in modo da accettare un numero indefinito di argomenti da sommare.

Modifichiamo il codice del test

				
					describe("Calculator class", () => {
    // Aggiorniamo la descrizione del test
    test('it should add multiple numbers', () => {
        const calc = new Calculator();
        expect(calc.add(2, 3)).toBe(5);
        // aggiungiamo i casi da supportare
        expect(calc.add(1,2,3,4)).toBe(10);
    });
})

				
			

Modifichiamo il codice nella maniera più semplice per far passare il test

				
					export class Calculator {
    add(...numbers) {
        let result = 0;
        for(let number of numbers) {
            result += number;
        }
        return result;
    }
}
				
			

Attraverso i test, otteniamo un metodo efficace per estendere una funzionalità senza creare regressioni sul funzionamento precedente.

La considerazione è sempre la stessa, è già visibile anche in questo esempio, ma è molto più apprezzabile su codice più complesso.

Refactoring con il TDD

Consideriamo di dover rifattorizzare il codice della funzione add per ottimizzarla.
In questo caso non abbiamo bisogno di scrivere un altro test.
Per rifattorizzare una funzione in TDD è sufficiente modificare la funzione stessa assicurandosi che i test continuino a passare.

				
					export class Calculator {
    add(...numbers) {
        return Math.sum(...numbers);
    }
}
				
			

Rilevamento più preciso di Bug ed Errori

Programmare implementando il Test Driven Development produce fin da subito una copertura dei test piuttosto alta, specialmente se in CI/CD sono state implementate regole di soglia che impediscono la pubblicazione di codice non testato o che presenta errori in fase di test.

Questo significa che una buona fetta di bug, specificamente i bug dovuti a regressioni nel codice, vengono prevenuti.
Gli altri però, durante lo sviluppo, devono essere risolti come al solito.

Consideriamo il caso in cui il client chiami la funzione add inserendo valori non numerici. In produzione avremo un comportamento inaspettato nonostante i test continuino a passare.

Ecco come risolvere un bug implementando il TDD:

Scriviamo il codice che descrive il bug:

				
					describe("Calculator class", () => {
    // ...
    test('it should throw if at least one argument is not a number', () => {
        const calc = new Calculator();
        expect(calc.add("hello", 3)).toThrow("All arguments must be of type numeric.");
    });
})
				
			

Come fatto fin’ora, scriviamo il codice minimo per soddisfare il requisito:

				
					export class Calculator {
    add(...numbers) {
        if(numbers.some(n => typeof n !== number)) {
            throw new Error("All arguments must be of type numeric.");
        }
        return Math.sum(...numbers);
    }
}
				
			

Incremento della Fiducia e Velocità nello Sviluppo

Attraverso l’approccio TDD, sviluppiamo fiducia nella stabilità del codice. I test costanti assicurano che le modifiche non causino regressioni, permettendoci di sviluppare nuove funzionalità con rapidità.

Conclusione

Il Test Driven Development (TDD) rappresenta un metodo potente per lo sviluppo, offrendo una serie di vantaggi tangibili. 

Con esempi pratici in JavaScript, abbiamo illustrato come il TDD potenzia la progettazione, riduce gli errori, agevola il refactoring e incrementa la fiducia nello sviluppo. 

Integrando il TDD nella propria pratica di sviluppo, è possibile garantire applicazioni di alta qualità e affidabilità. 

Hai domande o vuoi approfondimenti su questo argomento? Contattaci.

Condividi l'articolo: