Simulação e criação de dublês com Mockery
Isole unidades com dublês de teste flexíveis.
Simulação e criação de dublês com Mockery é uma aula grátis de PHP Academy no CoddyKit. Esta é a aula 2 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.
Por que uma biblioteca de simulação dedicada
O PHPUnit fornece sua própria API de dublês de teste, mas o Mockery oferece uma sintaxe mais fluente e expressiva, além de recursos que as simulações do PHPUnit não têm — simulações parciais, espiões, correspondência flexível de argumentos e expectativas ordenadas. Para códigos complexos com muitas colaborações, o Mockery costuma ser muito mais legível. Esta lição aborda todo o espectro de dublês e quando usar cada um.
composer require --dev mockery/mockeryDublê de retorno vs simulação vs espião
Um vocabulário preciso evita testes confusos:
- Dublê de retorno — retorna valores predefinidos; você verifica o estado (o resultado).
- Simulação — tem expectativas sobre chamadas; você verifica o comportamento (se foi chamada corretamente). Expectativas não atendidas fazem o teste falhar.
- Espião — registra chamadas e permite verificá-las depois do fato.
Como regra geral: prefira dublês de retorno para consultas e simulações ou espiões para comandos.
Um dublê de retorno com allows()
Use Mockery::mock() e allows() (ou shouldReceive()->andReturn()) para fornecer valores de retorno predefinidos sem verificar se a chamada ocorreu. Aqui, um dublê de uma porta de acesso fornece uma taxa de câmbio conhecida para que possamos testar o cálculo isoladamente.
<?php
use Mockery;
use PHPUnit\Framework\TestCase;
final class ConverterTest extends TestCase
{
public function test_converts_using_rate(): void
{
$rates = Mockery::mock(RateGateway::class);
$rates->allows()->rateFor('USD', 'EUR')->andReturn(0.9);
$sut = new Converter($rates);
self::assertSame(90.0, $sut->convert(100.0, 'USD', 'EUR'));
Mockery::close();
}
}
Uma simulação com expects()
Quando a própria interação é o contrato — por exemplo, "um e-mail deve ser enviado exatamente uma vez" — use expects() ou shouldReceive()->once(). O Mockery verifica a expectativa durante Mockery::close(); uma chamada não atendida faz o teste falhar.
<?php
use Mockery;
$mailer = Mockery::mock(Mailer::class);
$mailer->expects()
->send(Mockery::type(Message::class))
->once();
$service = new SignupService($mailer);
$service->register('ada@example.com');
Mockery::close(); // fails here if send() was never called
Comparadores de argumentos
Os comparadores do Mockery permitem que as expectativas sejam tão flexíveis ou rigorosas quanto necessário:
Mockery::any()— qualquer valor.Mockery::type('string')/ um nome de classe — verificação de tipo.Mockery::on(fn($a) => ...)— predicado personalizado.Mockery::capture($var)— captura o argumento para verificações posteriores.
<?php
use Mockery;
$repo = Mockery::mock(UserRepo::class);
$repo->expects()
->save(Mockery::on(fn(User $u) => $u->isActive()))
->once()
->andReturnTrue();
Sequências de retorno e retornos dinâmicos
Você pode programar vários valores de retorno em chamadas sucessivas ou calcular o retorno a partir dos argumentos com andReturnUsing(). Isso modela lógica de novas tentativas, paginação ou colaboradores com estado.
<?php
use Mockery;
$api = Mockery::mock(HttpClient::class);
// First call throws, second succeeds (retry test):
$api->shouldReceive('get')
->twice()
->andThrow(new \RuntimeException('timeout'))
->andReturn('{"ok":true}');
// Or derive the return from the input:
$api->shouldReceive('echo')
->andReturnUsing(fn(string $in) => strtoupper($in));
Espiões: verifique depois do fato
Um espião inverte a ordem: aja primeiro e verifique depois. Mockery::spy() registra chamadas; depois, você as consulta com shouldHaveReceived(). Os espiões mantêm limpa a estrutura Preparar-Agir-Verificar quando você não quer que expectativas predefinidas deixem a configuração confusa.
<?php
use Mockery;
$logger = Mockery::spy(Logger::class);
$service = new PaymentService($logger);
$service->charge(500);
// Assertions happen AFTER the action:
$logger->shouldHaveReceived('info')
->with(Mockery::pattern('/charged 500/'))
->once();
Mockery::close();
Simulações parciais
Às vezes, você quer um objeto real, mas com um método substituído — uma simulação parcial. O makePartial() do Mockery encaminha as chamadas para métodos reais, exceto aqueles para os quais você definiu expectativas. Use com moderação: a dependência excessiva de simulações parciais geralmente indica que uma classe está fazendo coisas demais.
<?php
use Mockery;
$report = Mockery::mock(Report::class)->makePartial();
// Only stub the slow/external method; the rest runs for real
$report->shouldReceive('fetchRawData')->andReturn(['a', 'b', 'c']);
// real summarize() runs, using the stubbed data
$summary = $report->summarize();
Não simule o que não é seu
Um princípio fundamental dos testes: evite simular diretamente classes de terceiros. As APIs delas podem mudar, e sua simulação pode se afastar silenciosamente da realidade. Em vez disso, envolva-as em uma interface própria e simule essa interface. A simulação então verifica seu contrato, e um teste de integração do adaptador verifica a vinculação real.
<?php
interface PaymentGateway { // you own this
public function charge(int $cents, string $token): string;
}
final class StripeGateway implements PaymentGateway {
public function __construct(private \Stripe\StripeClient $client) {}
public function charge(int $cents, string $token): string {
return $this->client->paymentIntents->create([/* ... */])->id;
}
}
// Tests mock PaymentGateway, never \Stripe\StripeClient directly.
Sempre feche e verifique as contagens
Dois procedimentos operacionais obrigatórios:
- Chame
Mockery::close()emtearDown()(ou use oMockeryPHPUnitIntegration) para que as expectativas sejam realmente verificadas e as variáveis globais sejam limpas. - Use contagens explícitas (
once(),times(n),never()) — simulações vagas deixam erros passarem.
<?php
use PHPUnit\Framework\TestCase;
use Mockery\Adapter\Phpunit\MockeryPHPUnitIntegration;
final class OrderServiceTest extends TestCase
{
use MockeryPHPUnitIntegration; // auto-calls Mockery::close()
public function test_does_not_refund_paid_orders(): void
{
$gw = \Mockery::mock(PaymentGateway::class);
$gw->shouldReceive('refund')->never();
// ... exercise SUT ...
}
}
Expectativas ordenadas
Às vezes, a ordem das chamadas faz parte do contrato — você precisa chamar beginTransaction() antes de commit(). O ordered() do Mockery impõe a sequência e faz o teste falhar se as chamadas chegarem fora de ordem. Use-o somente quando a ordem realmente importar; especificar a ordem em excesso produz testes frágeis.
<?php
use Mockery;
$tx = Mockery::mock(Transaction::class);
$tx->shouldReceive('begin')->once()->ordered();
$tx->shouldReceive('commit')->once()->ordered();
$service = new TransferService($tx);
$service->run();
Mockery::close(); // fails if commit() happened before begin()
Verificação rápida
Dublê de retorno ou simulação — qual usar em cada caso?
Recapitulação
Você dominou os dublês do Mockery:
- Dublês de retorno (
allows) para consultas; simulações (expects) para comandos; espiões para verificações posteriores. - Comparadores de argumentos (
type,on,capture) ajustam o rigor. - Sequências de retorno e
andReturnUsingmodelam colaboradores com estado ou dinâmicos. - Simulações parciais substituem métodos individuais; use-as com moderação.
- Não simule o que não é seu — envolva terceiros em uma interface própria.
- Sempre chame
Mockery::close()e verifique contagens explícitas de chamadas.
Próximo: testes de integração e funcionais.
Perguntas Frequentes
A aula “Simulação e criação de dublês com Mockery” é grátis?
Sim — o texto completo de “Simulação e criação de dublês com Mockery” é 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 “Simulação e criação de dublês com Mockery”?
Isole unidades com dublês de teste flexíveis. 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 2 de 4.
Quanto tempo leva a aula “Simulação e criação de dublês com Mockery”?
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