Por que testar sem mocks é como cozinhar sem sal
\n\nImagine que você está desenvolvendo uma aplicação que envia notificações por e-mail, interage com um banco de dados e consome uma API externa. Como testar tudo isso sem usar mocks e fixtures em PHP? Você teria que enviar e-mails reais, criar registros no banco de dados e aguardar respostas de serviços externos a cada execução. Isso é lento, instável e, francamente, ineficiente.
\n\nÉ aqui que entram os test doubles — substitutos para objetos reais. No ecossistema PHP, a combinação ideal é: mocks para verificar interações e fixtures para preparar os dados. Vamos entender como isso funciona na prática.
\n\nMock vs Stub vs Fake: entenda as diferenças
\n\nAntes de mergulharmos no código, é importante esclarecer os conceitos. No universo dos testes unitários em PHP, existem três tipos principais de substitutos:
\n\n- \n
- Stub — retorna dados pré-definidos. Você testa se seu código processa esses dados corretamente. \n
- Mock — além de retornar dados, verifica como seu sistema chama os métodos: quantas vezes, com quais argumentos e em qual ordem. \n
- Fake — uma implementação simplificada e funcional (como um banco de dados em memória). \n
No PHPUnit, que é o padrão de facto para testes em PHP, esses termos às vezes se misturam. Mas vamos ser precisos: mocks em PHP são aqueles onde você define expectativas (expectations).
\n\nCriando seu primeiro mock com PHPUnit
\n\nSuponha que temos uma classe Mailer responsável por enviar e-mails. Queremos verificar se o serviço de registro chama o método send() exatamente uma vez com os dados corretos.
<?php\nuse PHPUnit\\Framework\\TestCase;\n\nclass UserRegistrationTest extends TestCase\n{\n public function testSendWelcomeEmailOnRegistration(): void\n {\n // Criando um mock para Mailer\n $mailer = $this->createMock(Mailer::class);\n \n // Configurando a expectativa: método send será chamado 1 vez\n $mailer->expects($this->once())\n ->method('send')\n ->with(\n $this->equalTo('user@example.com'),\n $this->stringContains('Welcome')\n );\n \n $service = new UserRegistrationService($mailer);\n $service->register('user@example.com', 'password123');\n }\n}\n\n\nPerceba: não estamos testando se o e-mail foi realmente enviado. Estamos verificando se nosso código tentou enviá-lo — essa é a essência dos mocks em testes PHP. Se o método send() não for chamado (ou for chamado duas vezes), o teste falhará.
O método expects e suas possibilidades
\n\nO PHPUnit oferece uma API rica para configurar expectativas:
\n\n- \n
$this->once()— exatamente uma chamada \n $this->exactly(3)— exatamente três chamadas \n $this->atLeastOnce()— pelo menos uma chamada \n $this->never()— o método não deve ser chamado \n
Para os argumentos, use os matchers (constraints):
\n\n$mailer->expects($this->once())\n ->method('send')\n ->with(\n $this->callback(function($email) {\n return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;\n }),\n $this->anything() // não nos importamos com o segundo argumento\n );\n\n\nFixtures em PHP: preparando os dados antes que você precise
\n\nFixtures são estados de dados predefinidos nos quais o sistema se encontra antes da execução do teste. No PHPUnit, usamos os métodos setUp() e