0Pricing
PHP Academy · Lezione

Il flusso di lavoro dello sviluppo guidato dai test

Guidi la progettazione con il ciclo red-green-refactor

Il flusso di lavoro dello sviluppo guidato dai test è una lezione PHP Academy gratuita su CoddyKit. Questa è la lezione 1 di 4. Puoi leggere la lezione completa qui gratuitamente — poi esercitati direttamente nel browser con un editor di codice integrato e un tutor IA disponibile 24/7. Fa parte del percorso di apprendimento PHP Academy, e i tuoi progressi si sincronizzano tra il web e l'app CoddyKit. Il corso PHP Academy include 4 lezioni in totale.

Il TDD è uno strumento di progettazione

Il Test-Driven Development viene spesso presentato come una tecnica di testing, ma il suo vero valore è la pressione progettuale. Scrivendo prima il test, è costretto a definire l'interfaccia pubblica di un oggetto, le sue dipendenze e il suo contratto prima di scrivere l'implementazione. I test sono un prodotto secondario; il prodotto principale è una progettazione migliore.

Red, Green, Refactor

Il ciclo prevede tre fasi rigorose:

  • Red — scriva un test fallito per il prossimo comportamento minimo. Deve fallire per il motivo corretto.
  • Green — scriva il minimo codice necessario a superare il test, anche se è poco elegante.
  • Refactor — ripulisca l'implementazione e i test mantenendo i test verdi.

La disciplina è importante: non salti mai Red, perché potrebbe verificare nulla, e non costruisca più del necessario in Green.

Red: scrivere prima il test fallito

Costruiremo un PriceCalculator che applichi uno sconto percentuale. Inizi con un test per un comportamento che ancora non esiste. Eseguendolo, dovrebbe fallire perché la classe non è definita: questo è un Red valido.

<?php
use PHPUnit\Framework\TestCase;

final class PriceCalculatorTest extends TestCase
{
    public function test_applies_a_percentage_discount(): void
    {
        $calc = new PriceCalculator();
        // 100 with 10% off -> 90.0
        self::assertSame(90.0, $calc->withDiscount(100.0, 10));
    }
}

Green: il minimo codice per superare il test

Ora scriva l'implementazione più piccola possibile per portare il test in Green. Resista alla tentazione di aggiungere regole di arrotondamento, validazione o gestione delle valute: non c'è ancora alcun test fallito che lo richieda.

<?php
final class PriceCalculator
{
    public function withDiscount(float $amount, float $percent): float
    {
        return $amount - ($amount * $percent / 100);
    }
}

$calc = new PriceCalculator();
var_dump($calc->withDiscount(100.0, 10)); // float(90)

Usare la triangolazione per ottenere generalità

Un singolo test può essere soddisfatto con un valore hard-coded. La triangolazione, cioè l'aggiunta di un secondo esempio diverso, costringe l'implementazione a generalizzare. Aggiunga un caso che la soluzione banale non possa simulare, facendo emergere la formula reale.

<?php
use PHPUnit\Framework\TestCase;

final class PriceCalculatorTest extends TestCase
{
    public function test_zero_percent_is_unchanged(): void
    {
        self::assertSame(50.0, (new PriceCalculator())->withDiscount(50.0, 0));
    }

    public function test_full_discount_is_free(): void
    {
        self::assertSame(0.0, (new PriceCalculator())->withDiscount(50.0, 100));
    }
}

Usare i casi limite come specifiche

Nel TDD, un nuovo requisito corrisponde a un nuovo test fallito. Supponiamo che gli sconti negativi non siano validi. Scriva prima il test che si aspetta un'eccezione: fallisce perché per ora non viene generata alcuna eccezione. Il test documenta il contratto.

<?php
use PHPUnit\Framework\TestCase;

final class PriceCalculatorValidationTest extends TestCase
{
    public function test_rejects_negative_discount(): void
    {
        $this->expectException(\InvalidArgumentException::class);
        (new PriceCalculator())->withDiscount(100.0, -5);
    }
}

Di nuovo Green, poi Refactor

Aggiunga il controllo necessario a superare il nuovo test, quindi esegua il refactoring con fiducia: i test esistenti rileveranno le regressioni. Noti che abbiamo introdotto l'arrotondamento solo dopo che un test, o un requisito noto, lo ha giustificato.

<?php
final class PriceCalculator
{
    public function withDiscount(float $amount, float $percent): float
    {
        if ($percent < 0 || $percent > 100) {
            throw new \InvalidArgumentException('percent must be 0..100');
        }
        return round($amount - ($amount * $percent / 100), 2);
    }
}

var_dump((new PriceCalculator())->withDiscount(19.99, 15)); // float(16.99)

I data provider condensano gli esempi

Quando un comportamento è stabile, riunisca molte coppie di esempi in un unico test parametrizzato con un data provider. In questo modo il ciclo Red/Green resta rapido e la tabella delle intenzioni è facile da leggere.

<?php
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\DataProvider;

final class DiscountTableTest extends TestCase
{
    #[DataProvider('cases')]
    public function test_discounts(float $amount, float $pct, float $expected): void
    {
        self::assertSame($expected, (new PriceCalculator())->withDiscount($amount, $pct));
    }

    public static function cases(): array
    {
        return [
            'no discount'   => [100.0, 0, 100.0],
            'ten percent'   => [100.0, 10, 90.0],
            'free'          => [100.0, 100, 0.0],
        ];
    }
}

Mantenere rapido il ciclo

Il TDD funziona solo se il ciclo di feedback dura pochi secondi, non minuti. Le leve pratiche sono:

  • Esegua un singolo file o applichi un filtro: phpunit --filter test_full_discount_is_free.
  • Utilizzi --testdox per leggere i test come una specifica del comportamento.
  • Mantenga i test unitari privi di I/O: niente database, rete o file system nel ciclo interno.
vendor/bin/phpunit --testdox --filter PriceCalculator

Eseguire il refactoring anche dei test

La fase Refactor riguarda la suite di test, non solo il codice di produzione. Elimini la duplicazione con helper e setUp(), assegni ai test nomi basati sul comportamento ed elimini i test che non verificano più nulla di significativo. I test sono codice da mantenere per sempre: li tratti con la stessa cura.

<?php
use PHPUnit\Framework\TestCase;

final class PriceCalculatorTest extends TestCase
{
    private PriceCalculator $calc;

    protected function setUp(): void
    {
        $this->calc = new PriceCalculator(); // shared setup, no duplication
    }

    public function test_ten_percent(): void
    {
        self::assertSame(90.0, $this->calc->withDiscount(100.0, 10));
    }
}

Il TDD modella le dipendenze

Poiché scrive prima il test, le dipendenze problematiche diventano subito evidenti. Se una classe è difficile da istanziare in un test, è un'indicazione progettuale: inietti i collaboratori invece di crearli internamente con new. Il calcolatore dello sconto riportato di seguito diventa testabile accettando la propria politica di arrotondamento, anziché codificarla direttamente: il TDD ha portato all'introduzione di questo punto di estensione.

<?php
interface RoundingPolicy { public function round(float $v): float; }

final class PriceCalculator
{
    public function __construct(private RoundingPolicy $rounding) {}

    public function withDiscount(float $amount, float $percent): float
    {
        return $this->rounding->round($amount - ($amount * $percent / 100));
    }
}
// Tests inject a deterministic RoundingPolicy; no hidden global behavior.

Controllo rapido

Qual è lo scopo della fase Red?

Riepilogo

Ha praticato il TDD come disciplina di progettazione:

  • Red-Green-Refactor: test fallito, codice minimo e poi ripulitura mantenendo i test verdi.
  • La triangolazione costringe alla generalità; i nuovi requisiti arrivano sotto forma di nuovi test falliti.
  • I data provider condensano i casi stabili; --filter/--testdox mantengono rapido il ciclo.
  • Esegua il refactoring anche dei test: sono codice destinato a durare a lungo.

Prossimo argomento: test double flessibili con Mockery.

Domande Frequenti

La lezione «Il flusso di lavoro dello sviluppo guidato dai test» è gratuita?

Sì — il testo completo di «Il flusso di lavoro dello sviluppo guidato dai test» è gratuito qui sul web. Per esercitarvi in modo interattivo (un editor di codice integrato e un tutor IA 24/7) e sbloccare il resto del corso PHP Academy, passa a CoddyKit PRO. Il corso PHP Academy include 4 lezioni in totale.

Cosa imparerò in «Il flusso di lavoro dello sviluppo guidato dai test»?

Guidi la progettazione con il ciclo red-green-refactor Eserciti PHP Academy con codice pratico che esegui direttamente nel browser, e un tutor IA 24/7 risponde alle tue domande mentre lavori sulla lezione.

Ho bisogno di esperienza per iniziare PHP Academy?

Non è richiesta alcuna esperienza precedente. PHP Academy su CoddyKit è strutturato per principianti e studenti avanzati, quindi puoi iniziare da qui o dall'inizio e procedere al tuo ritmo. Questa è la lezione 1 di 4.

Quanto tempo richiede la lezione «Il flusso di lavoro dello sviluppo guidato dai test»?

La maggior parte delle lezioni CoddyKit richiede circa 5–10 minuti. Ogni lezione è breve e interattiva, quindi fai progressi costanti e riprendi esattamente da dove hai lasciato su web e app.

Posso scrivere ed eseguire codice in questa lezione PHP Academy?

Sì. Ogni lezione PHP Academy include un editor di codice integrato, quindi scrivi ed esegui codice reale direttamente nel tuo browser e ricevi feedback istantaneo dall'IA — nessuna configurazione locale necessaria.

Tutte le lezioni di questo corso

  1. Il flusso di lavoro dello sviluppo guidato dai test
  2. Mock e stub con Mockery
  3. Test di integrazione e funzionali
  4. Mutation testing con Infection
← Torna a PHP Academy