Skip to content

Commit 46a4bc3

Browse files
committed
support unit instance mocking
Also prioritises the dispatching of unit instances, now that the preferred way of running units is with the new PHP syntax specifying the arguments by name.
1 parent f54a210 commit 46a4bc3

File tree

2 files changed

+90
-8
lines changed

2 files changed

+90
-8
lines changed

src/Bus/UnitDispatcher.php

+28-8
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
namespace Lucid\Bus;
44

5+
use App;
6+
use Lucid\Testing\UnitMock;
7+
use Lucid\Testing\UnitMockRegistry;
58
use ReflectionClass;
69
use Lucid\Units\Job;
710
use ReflectionException;
@@ -31,22 +34,39 @@ trait UnitDispatcher
3134
*/
3235
public function run($unit, $arguments = [], $extra = [])
3336
{
34-
if ($arguments instanceof Request) {
37+
if (is_object($unit) && !App::runningUnitTests()) {
38+
$result = $this->dispatch($unit);
39+
} elseif ($arguments instanceof Request) {
3540
$result = $this->dispatch($this->marshal($unit, $arguments, $extra));
3641
} else {
3742
if (!is_object($unit)) {
3843
$unit = $this->marshal($unit, new Collection(), $arguments);
39-
}
4044

41-
if ($unit instanceof Operation) {
42-
event(new OperationStarted(get_class($unit), $arguments));
43-
}
45+
// don't dispatch unit when in tests and have a mock for it.
46+
} elseif (App::runningUnitTests() && app(UnitMockRegistry::class)->has(get_class($unit))) {
47+
/** @var UnitMock $mock */
48+
$mock = app(UnitMockRegistry::class)->get(get_class($unit));
49+
$mock->compareTo($unit);
4450

45-
if ($unit instanceof Job) {
46-
event(new JobStarted(get_class($unit), $arguments));
51+
// Reaching this step confirms that the expected mock is similar to the passed instance, so we
52+
// get the unit's mock counterpart to be dispatched. Otherwise, the previous step would
53+
// throw an exception when the mock doesn't match the passed instance.
54+
$unit = $this->marshal(
55+
get_class($unit),
56+
new Collection(),
57+
$mock->getConstructorExpectationsForInstance($unit)
58+
);
4759
}
4860

49-
$result = $this->dispatch($unit, $arguments);
61+
$result = $this->dispatch($unit);
62+
}
63+
64+
if ($unit instanceof Operation) {
65+
event(new OperationStarted(get_class($unit), $arguments));
66+
}
67+
68+
if ($unit instanceof Job) {
69+
event(new JobStarted(get_class($unit), $arguments));
5070
}
5171

5272
return $result;

src/Testing/UnitMock.php

+62
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,42 @@ public function setConstructorExpectations(array $constructorExpectations)
5656
$this->constructorExpectations[] = $this->currentConstructorExpectations;
5757
}
5858

59+
public function getConstructorExpectations()
60+
{
61+
return $this->constructorExpectations;
62+
}
63+
64+
/**
65+
* Returns constructor expectations array that matches the given $unit.
66+
* Empty array otherwise.
67+
*
68+
* @param $unit
69+
* @return array|mixed|void
70+
* @throws ReflectionException
71+
*/
72+
public function getConstructorExpectationsForInstance($unit)
73+
{
74+
foreach ($this->constructorExpectations as $index => $args) {
75+
$expected = new $unit(...$args);
76+
77+
$ref = new ReflectionClass($unit);
78+
79+
// we start by assuming that the unit instance and the $expected one are equal
80+
// until proven otherwise when we find differences between properties.
81+
$isEqual = true;
82+
foreach ($ref->getProperties() as $property) {
83+
if ($property->getValue($unit) !== $property->getValue($expected)) {
84+
$isEqual = false;
85+
break;
86+
}
87+
}
88+
89+
if ($isEqual) {
90+
return $this->constructorExpectations[$index];
91+
}
92+
}
93+
}
94+
5995
/**
6096
* @return array
6197
* @throws ReflectionException
@@ -111,6 +147,32 @@ private function registerMock(): UnitMock
111147
return $this;
112148
}
113149

150+
/**
151+
* Compare the mock to an actual instance.
152+
*
153+
* @param object $unit
154+
* @return void
155+
* @throws Mockery\Exception\NoMatchingExpectationException
156+
*/
157+
public function compareTo(object $unit)
158+
{
159+
$expected = array_map(fn($args) => new $unit(...$args), $this->constructorExpectations);
160+
161+
$ref = new ReflectionClass($unit);
162+
foreach ($ref->getProperties() as $property) {
163+
164+
$expectations = array_map(fn($instance) => $property->getValue($instance), $expected);
165+
166+
if (!in_array($property->getValue($unit), $expectations)) {
167+
throw new Mockery\Exception\NoMatchingExpectationException(
168+
"Mismatch in \${$property->getName()} when running {$this->unit} \n\n--- Expected (one of)\n".
169+
print_r(join("\n", array_map(fn($instance) => $property->getValue($instance), $expected)), true).
170+
"\n\n+++Actual:\n".print_r($property->getValue($unit), true)."\n\n"
171+
);
172+
}
173+
}
174+
}
175+
114176
public function getMock(): MockInterface
115177
{
116178
$this->registerMock();

0 commit comments

Comments
 (0)