पहचान
ठीक है, मैं देखता हूं कि मॉकरी के लिए एक समाधान प्रदान किया गया है, इसलिए जैसा कि मुझे मॉकरी पसंद नहीं है, मैं आपको एक भविष्यवाणी विकल्प देने जा रहा हूं, लेकिन मैं आपको पहले सुझाव दूंगा कि आप मॉकरी और भविष्यवाणी के बीच के अंतर के बारे में पढ़ें।
लंबी कहानी छोटी : "भविष्यवाणी संदेश बंधन नामक दृष्टिकोण का उपयोग करती है - इसका मतलब है कि विधि का व्यवहार समय के साथ नहीं बदलता है, बल्कि दूसरी विधि द्वारा बदल दिया जाता है।"
वास्तविक विश्व को कवर करने के लिए समस्याग्रस्त कोड
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;
}
}
PhpUnit भविष्यवाणी समाधान
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);
}
}
सारांश
एक बार फिर, भविष्यवाणी अधिक भयानक है! मेरी चाल भविष्यवाणी के संदेशवाहक बंधन प्रकृति का लाभ उठाने के लिए है और भले ही यह दुख की बात है कि यह एक विशिष्ट, कॉलबैक जावास्क्रिप्ट नरक कोड की तरह है, जिसकी शुरुआत $ स्वयं = $ से होती है; जैसा कि आप शायद ही कभी इकाई परीक्षणों को इस तरह लिखते हैं, मुझे लगता है कि यह एक अच्छा समाधान है और यह निश्चित रूप से पालन करना आसान है, डीबग करना, क्योंकि यह वास्तव में कार्यक्रम के निष्पादन का वर्णन करता है।
BTW: एक दूसरा विकल्प है, लेकिन हमारे द्वारा परीक्षण किए जा रहे कोड को बदलने की आवश्यकता है। हम संकटमोचन को लपेट कर एक अलग वर्ग में ले जा सकते हैं:
$chunk->processingInProgress();
$this->chunksStorage->updateChunk($chunk);
के रूप में लिपटे जा सकता है:
$processorChunkStorage->persistChunkToInProgress($chunk);
और ऐसा ही है, लेकिन जैसा कि मैं इसके लिए एक और वर्ग बनाना नहीं चाहता था, मैं पहले वाले को पसंद करता हूं।