0Pricing
PHP Academy · Aula

O fluxo de trabalho do desenvolvimento orientado por testes

Oriente o design pelo ciclo vermelho-verde-refatoração.

O fluxo de trabalho do desenvolvimento orientado por testes é uma aula grátis de PHP Academy no CoddyKit. Esta é a aula 1 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de PHP Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de PHP Academy inclui 4 aulas no total.

TDD é uma ferramenta de projeto

O Desenvolvimento Orientado por Testes costuma ser apresentado como uma técnica de testes, mas seu valor real é a pressão de projeto. Ao escrever o teste primeiro, você é obrigado a definir a interface pública de um objeto, suas dependências e seu contrato antes de escrever qualquer implementação. Os testes são um subproduto; um projeto melhor é o produto.

Vermelho, Verde, Refatorar

O ciclo tem três fases rigorosas:

  • Vermelho — escreva um teste que falhe para o próximo comportamento pequeno. Ele deve falhar pelo motivo certo.
  • Verde — escreva o mínimo de código necessário para passar, mesmo que fique feio.
  • Refatorar — limpe a implementação e os testes sem deixar de estar no verde.

Disciplina é importante: nunca pule o Vermelho (você pode estar testando nada) e nunca crie mais do que o necessário no Verde.

Vermelho: escreva primeiro o teste que falha

Vamos criar um PriceCalculator que aplica um desconto percentual. Comece com um teste para um comportamento que ainda não existe. Executá-lo deve falhar porque a classe não está definida — esse é um Vermelho válido.

<?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));
    }
}

Verde: o código mínimo para passar

Agora escreva a menor implementação que deixa a barra verde. Resista a adicionar regras de arredondamento, validação ou tratamento de moedas — ainda não há nenhum teste falhando que exija isso.

<?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)

Use a triangulação para alcançar generalidade

Um único teste pode ser satisfeito com valores fixados diretamente. A triangulação — adicionar um segundo exemplo diferente — força a implementação a generalizar. Adicione um caso que a solução trivial não consiga falsificar, fazendo surgir a fórmula real.

<?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));
    }
}

Transforme casos-limite em especificações

Em TDD, um novo requisito é um novo teste que falha. Suponha que descontos negativos sejam inválidos. Escreva primeiro o teste que espera uma exceção; ele falha porque nada lança uma exceção ainda. O teste documenta o contrato.

<?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);
    }
}

Verde novamente, depois refatore

Adicione a proteção para fazer o novo teste passar e, em seguida, refatore com confiança — os testes existentes detectam regressões. Observe que também introduzimos o arredondamento somente depois que um teste (ou um requisito conhecido) o justificou.

<?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)

Provedores de dados condensam exemplos

Quando um comportamento estiver estável, reúna muitos pares de exemplos em um único teste parametrizado com um provedor de dados. Isso mantém o ciclo Vermelho/Verde rápido e a tabela de intenções legível.

<?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],
        ];
    }
}

Mantenha o ciclo rápido

O TDD só funciona se o ciclo de feedback durar segundos, não minutos. Recursos práticos:

  • Execute um único arquivo ou aplique um filtro: phpunit --filter test_full_discount_is_free.
  • Use --testdox para ler os testes como uma especificação de comportamento.
  • Mantenha os testes unitários livres de E/S — sem banco de dados, rede ou sistema de arquivos no ciclo interno.
vendor/bin/phpunit --testdox --filter PriceCalculator

Refatore os testes também

A fase de Refatoração abrange a suíte de testes, não apenas o código de produção. Remova duplicações com auxiliares e setUp(), nomeie os testes de acordo com o comportamento e exclua os testes que já não verificam nada significativo. Os testes são código que você manterá para sempre — trate-os com o mesmo cuidado.

<?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));
    }
}

O TDD molda as dependências

Como você escreve o teste primeiro, as dependências inadequadas se tornam evidentes imediatamente. Se uma classe for difícil de instanciar em um teste, isso é um feedback de projeto: injete colaboradores em vez de criá-los com new dentro da classe. A calculadora de descontos abaixo se torna testável ao aceitar sua política de arredondamento, em vez de codificá-la diretamente — o TDD levou essa separação a existir.

<?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.

Verificação rápida

Qual é o objetivo da etapa Vermelho?

Recapitulação

Você praticou TDD como uma disciplina de projeto:

  • Vermelho-Verde-Refatorar: teste que falha, código mínimo e depois limpeza enquanto permanece no verde.
  • A triangulação força a generalidade; novos requisitos chegam como novos testes que falham.
  • Provedores de dados condensam casos estáveis; --filter/--testdox mantêm o ciclo rápido.
  • Refatore também os testes — eles são código de longa duração.

Próximo: dublês de teste flexíveis com Mockery.

Perguntas Frequentes

A aula “O fluxo de trabalho do desenvolvimento orientado por testes” é grátis?

Sim — o texto completo de “O fluxo de trabalho do desenvolvimento orientado por testes” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de PHP Academy, atualize para CoddyKit PRO. O curso de PHP Academy inclui 4 aulas no total.

O que vou aprender em “O fluxo de trabalho do desenvolvimento orientado por testes”?

Oriente o design pelo ciclo vermelho-verde-refatoração. Você pratica PHP Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar PHP Academy?

Nenhuma experiência prévia é necessária. PHP Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 1 de 4.

Quanto tempo leva a aula “O fluxo de trabalho do desenvolvimento orientado por testes”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de PHP Academy?

Sim. Cada aula de PHP Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. O fluxo de trabalho do desenvolvimento orientado por testes
  2. Simulação e criação de dublês com Mockery
  3. Testes de integração e funcionais
  4. Testes de mutação com Infection
← Voltar para PHP Academy