Intro
Oke saya melihat ada satu solusi yang disediakan untuk Mockery, jadi karena saya tidak suka Mockery, saya akan memberi Anda alternatif Nubuat tetapi saya sarankan Anda terlebih dahulu membaca tentang perbedaan antara Mockery dan Prophecy terlebih dahulu.
Singkat cerita : "Nubuatan menggunakan pendekatan yang disebut pengikatan pesan - itu berarti bahwa perilaku metode tidak berubah seiring waktu, tetapi diubah oleh metode lain."
Kode bermasalah dunia nyata untuk dibahas
class Processor
{
/**
* @var MutatorResolver
*/
private $mutatorResolver;
/**
* @var ChunksStorage
*/
private $chunksStorage;
/**
* @param MutatorResolver $mutatorResolver
* @param ChunksStorage $chunksStorage
*/
public function __construct(MutatorResolver $mutatorResolver, ChunksStorage $chunksStorage)
{
$this->mutatorResolver = $mutatorResolver;
$this->chunksStorage = $chunksStorage;
}
/**
* @param Chunk $chunk
*
* @return bool
*/
public function process(Chunk $chunk): bool
{
$mutator = $this->mutatorResolver->resolve($chunk);
try {
$chunk->processingInProgress();
$this->chunksStorage->updateChunk($chunk);
$mutator->mutate($chunk);
$chunk->processingAccepted();
$this->chunksStorage->updateChunk($chunk);
}
catch (UnableToMutateChunkException $exception) {
$chunk->processingRejected();
$this->chunksStorage->updateChunk($chunk);
// Log the exception, maybe together with Chunk insert them into PostProcessing Queue
}
return false;
}
}
Solusi PhpUnit Prophecy
class ProcessorTest extends ChunkTestCase
{
/**
* @var Processor
*/
private $processor;
/**
* @var MutatorResolver|ObjectProphecy
*/
private $mutatorResolverProphecy;
/**
* @var ChunksStorage|ObjectProphecy
*/
private $chunkStorage;
public function setUp()
{
$this->mutatorResolverProphecy = $this->prophesize(MutatorResolver::class);
$this->chunkStorage = $this->prophesize(ChunksStorage::class);
$this->processor = new Processor(
$this->mutatorResolverProphecy->reveal(),
$this->chunkStorage->reveal()
);
}
public function testProcessShouldPersistChunkInCorrectStatusBeforeAndAfterTheMutateOperation()
{
$self = $this;
// Chunk is always passed with ACK_BY_QUEUE status to process()
$chunk = $this->createChunk();
$chunk->ackByQueue();
$campaignMutatorMock = $self->prophesize(CampaignMutator::class);
$campaignMutatorMock
->mutate($chunk)
->shouldBeCalled();
$this->mutatorResolverProphecy
->resolve($chunk)
->shouldBeCalled()
->willReturn($campaignMutatorMock->reveal());
$this->chunkStorage
->updateChunk($chunk)
->shouldBeCalled()
->will(
function($args) use ($self) {
$chunk = $args[0];
$self->assertTrue($chunk->status() === Chunk::STATUS_PROCESSING_IN_PROGRESS);
$self->chunkStorage
->updateChunk($chunk)
->shouldBeCalled()
->will(
function($args) use ($self) {
$chunk = $args[0];
$self->assertTrue($chunk->status() === Chunk::STATUS_PROCESSING_UPLOAD_ACCEPTED);
return true;
}
);
return true;
}
);
$this->processor->process($chunk);
}
}
Ringkasan
Sekali lagi, Nubuat lebih dahsyat! Trik saya adalah memanfaatkan sifat pengikatan pesan dari Prophecy dan meskipun sayangnya itu terlihat seperti kode neraka javascript panggilan balik yang khas, dimulai dengan $ self = $ this; karena Anda sangat jarang harus menulis unit test seperti ini, saya pikir ini adalah solusi yang bagus dan pasti mudah diikuti, debug, karena ini benar-benar menggambarkan eksekusi program.
BTW: Ada alternatif kedua tetapi perlu mengubah kode yang kami uji. Kita bisa membungkus pembuat onar dan memindahkan mereka ke kelas terpisah:
$chunk->processingInProgress();
$this->chunksStorage->updateChunk($chunk);
bisa dibungkus sebagai:
$processorChunkStorage->persistChunkToInProgress($chunk);
dan hanya itu, tetapi karena saya tidak ingin membuat kelas lain untuk itu, saya lebih suka yang pertama.