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
--testdoxpara 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 PriceCalculatorRefatore 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/--testdoxmantê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
- O fluxo de trabalho do desenvolvimento orientado por testes
- Simulação e criação de dublês com Mockery
- Testes de integração e funcionais
- Testes de mutação com Infection