November 29, 2025 (5mo ago) — last updated December 31, 2025 (4mo ago)

Red‑Green‑Refactor: Guida pratica al TDD

Impara il ciclo Red‑Green‑Refactor del TDD con esempi pratici in React/TypeScript, consigli di adozione e benefici per la qualità del codice.

← Back to blog
Cover Image for Red‑Green‑Refactor: Guida pratica al TDD

Impara il ciclo Red‑Green‑Refactor del TDD con esempi pratici, consigli di adozione e suggerimenti per integrare i test nel tuo workflow quotidiano. Ideale per team React/TypeScript.

Red‑Green‑Refactor TDD: Guida pratica

Sommario: Padroneggia il ciclo Red‑Green‑Refactor del TDD con esempi pratici in React/TypeScript, consigli di adozione e benefici misurabili per la qualità del codice.

Introduzione

Il ciclo Red‑Green‑Refactor del Test‑Driven Development (TDD) è un workflow semplice e disciplinato che trasforma l’incertezza in piccoli passi verificabili. Si parte scrivendo un test che fallisce (Red), si implementa il minimo necessario per farlo passare (Green) e si pulisce l’implementazione (Refactor). Questo approccio guida la progettazione, riduce il rischio di regressioni e rende il codice più manutenibile fin dall’inizio.

Perché il TDD è più di una tecnica di testing

Molti sviluppatori pensano al TDD solo come a una forma di test, ma è soprattutto una pratica di progettazione che modifica il flusso mentale: pensare prima all’uso, poi all’implementazione. Adottare il ciclo Red‑Green‑Refactor favorisce progressi piccoli e sicuri e aiuta i team a mantenere alto il ritmo di sviluppo, anche su codebase complesse1.

Le tre fasi — in pratica

  • Fase Red (test che fallisce): definisci un singolo comportamento osservabile e scrivi il test che lo verifica. Il test deve fallire per dimostrare che stai testando qualcosa di nuovo.
  • Fase Green (farlo passare): scrivi il codice minimo per far passare il test. Punta alla semplicità, non all’eleganza.
  • Fase Refactor (migliorare il codice): con i test verdi come rete di sicurezza, migliora nomi, struttura e rimuovi duplicazioni.

Non saltare il refactor: saltarlo accumula debito tecnico e ostacola i cambiamenti futuri.

Il ciclo TDD sul codice: esempio pratico con React + TypeScript

Per vedere il ciclo in azione, costruiremo un componente LikeButton usando TypeScript, React e Jest. L’esempio mostra come il TDD guidi decisioni di design e garantisca comportamento prevedibile.

Fase Red: scrivere il primo test

Il requisito iniziale: il componente si renderizza senza crash e mostra “Like”. Scrivi il test prima del componente.

// LikeButton.test.tsx
import React from 'react'
import { render, screen } from '@testing-library/react'
import LikeButton from './LikeButton'

describe('LikeButton', () => {
  it('renders a button with the initial text "Like"', () => {
    render(<LikeButton />)
    const likeButton = screen.getByRole('button', { name: /like/i })
    expect(likeButton).toBeInTheDocument()
  })
})

Eseguendo i test il caso fallirà perché il componente non esiste ancora: questa è la fase Red.

Fase Green: il minimo per far passare il test

Crea il componente minimo necessario.

// LikeButton.tsx
import React from 'react'

const LikeButton = () => {
  return <button>Like</button>
}

export default LikeButton

Esegui i test: ora dovrebbero essere verdi.

Fase Refactor: migliorare l’implementazione

Aggiungi tipi e prepara il componente per estensioni future.

// LikeButton.tsx (refactored)
import React, { FC } from 'react'

type LikeButtonProps = {}

const LikeButton: FC<LikeButtonProps> = () => {
  return <button>Like</button>
}

export default LikeButton

I test restano verdi e hai ora una base più chiara per aggiungere funzionalità.

Iterazione: cliccare il bottone

Nuovo requisito: al clic il testo diventa “Liked” e il bottone viene disabilitato.

// LikeButton.test.tsx (aggiunta)
import { fireEvent } from '@testing-library/react'

it('changes text to "Liked" and becomes disabled when clicked', () => {
  render(<LikeButton />)
  const likeButton = screen.getByRole('button', { name: /like/i })
  fireEvent.click(likeButton)
  expect(likeButton).toHaveTextContent('Liked')
  expect(likeButton).toBeDisabled()
})

Implementa il comportamento minimo:

// LikeButton.tsx (implementazione del clic)
import React, { FC, useState } from 'react'

type LikeButtonProps = {}

const LikeButton: FC<LikeButtonProps> = () => {
  const [liked, setLiked] = useState(false)
  const handleClick = () => setLiked(true)
  return (
    <button onClick={handleClick} disabled={liked}>
      {liked ? 'Liked' : 'Like'}
    </button>
  )
}

export default LikeButton

Ripeti: un piccolo requisito alla volta, protetto dai test.

Il caso aziendale per la qualità del codice

I benefici ingegneristici del TDD si traducono in valore di business. Ridurre difetti in produzione abbassa i costi di supporto e aumenta la fiducia degli utenti. Scrivere test prima del codice limita le regressioni e rende le modifiche future meno rischiose. Studi empirici e report di settore documentano miglioramenti misurabili nella qualità per team che applicano pratiche di testing disciplinate2.

Le pratiche correlate, come pipeline CI/CD e processi DevOps, consentono ai team performanti di rilasciare più frequentemente e con maggiore affidabilità, migliorando metriche di lead time e recupero dagli incidenti3.

Ridurre difetti e costi di manutenzione

Aggiungere test prima dell’implementazione limita il codice superfluo in produzione e crea una rete di sicurezza che riduce le regressioni. Nel tempo questo abbassa il costo totale di possesso del software e riduce il debito tecnico2.

Accelerare l’onboarding e la prevedibilità

Una suite di test ben curata funge da documentazione eseguibile: i nuovi sviluppatori possono eseguire i test per capire il comportamento atteso. Questo riduce i tempi di ramp‑up e rende le stime e il monitoraggio del lavoro più affidabili, migliorando la pianificazione con gli stakeholder3.

Anti‑pattern comuni e come evitarli

Test di integrazione travestiti da unit test

Problema: un test finisce per esercitare troppe dipendenze, diventando lento e fragile. Soluzione: isola l’unità usando mock, stub o fake. Riserva i test di integrazione per suite separate e più lente.

Testare l’implementazione invece del comportamento

Problema: i test asseriscono dettagli interni e si rompono quando si refattorizza. Soluzione: testa l’API pubblica e gli effetti osservabili; verifica input e output attesi.

Saltare il refactor

Problema: si passa alla prossima funzionalità lasciando codice disordinato. Soluzione: considera il refactor come passaggio obbligatorio dopo che i test sono verdi.

Integrare il TDD in team e codebase legacy

Adottare il TDD è un cambiamento culturale oltre che tecnico. Rendi i test parte della Definition of Done, promuovi pair programming e sessioni pratiche per diffondere il know‑how.

Promuovere il TDD nel team

Metodi pratici:

  • Pair programming per trasmettere il ciclo Red‑Green‑Refactor.
  • Mob programming per problemi complessi, ruotando chi guida.
  • Sessioni hands‑on per mostrare TDD sul codebase reale.

Inizia in piccolo: una nuova feature o un bug non critico sono ottimi punti di partenza.

Domare il codice legacy

Quando il codice non ha test, scrivi characterization test per fissare il comportamento corrente. Questi test ti permettono di refattorizzare con fiducia.

Automazione: CI/CD e qualità

Esegui la suite di test ad ogni commit in CI per avere feedback immediati e impedire regressioni in fase di merge. L’automazione mantiene il ciclo di feedback rapido e affidabile. Vedi anche: Guida al testing end‑to‑end e Pipeline CI/CD.

Tre domande rapide sul TDD

Il TDD sostituisce altri tipi di testing?

No. Il TDD si concentra sui unit test come strumento di progettazione, ma restano necessari test di integrazione e end‑to‑end per convalidare le interazioni e i flussi utente completi.

Come testare codice che usa database o API esterne?

Isola la logica di business usando mock, stub o fake. Mantieni i test unitari veloci e scrivi test di integrazione separati per verificare le interazioni reali.

Ha senso testare componenti UI semplici?

Sì, se testi il comportamento osservabile: cosa vede e cosa può fare l’utente. Evita di verificare dettagli di implementazione interni.

Q&A sintetica

D: Quanto tempo per vedere valore dal TDD?

R: Spesso si vedono riduzioni di regressioni e debug più rapidi entro poche sprint, dipendendo dalla disciplina del team.

D: Qual è il passo minimo per iniziare?

R: Inizia con un singolo nuovo requisito: scrivi il test che fallisce, implementa il minimo per farlo passare e poi refattorizza.

D: Come convincere gli stakeholder?

R: Mostra risparmi a lungo termine con esempi concreti di incidenti evitati e costi di manutenzione ridotti; usa metriche dalla CI e dai post‑mortem.

1.
Digital.ai, “State of Agile Report,” Digital.ai, https://digital.ai/resource-center/state-of-agile-report
2.
Victor R. Basili et al., “An Empirical Study on the Effects of Test-Driven Development,” https://link.springer.com/article/10.1007/s10664-015-9378-2
3.
Google Cloud, “State of DevOps Report,” https://cloud.google.com/devops/state-of-devops
4.
Martin Fowler, “TestDrivenDevelopment,” https://martinfowler.com/bliki/TestDrivenDevelopment.html
← Back to blog
🙋🏻‍♂️

L'AI scrive codice.
Tu lo fai durare.

Nell'era dell'accelerazione AI, il codice pulito non è solo una buona pratica — è la differenza tra sistemi che si scalano e codebase che collassano sotto il loro stesso peso.