Last active
February 18, 2025 02:40
-
-
Save diloabininyeri/ec59712b2dddaf3161eb843e400a72ae to your computer and use it in GitHub Desktop.
builder a mock object and run test with it with the php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
class MockBuilder | |
{ | |
private array $methodMocks = []; | |
public function getInstance(string $originalClass): object | |
{ | |
$mockClassName = $this->generateUniqueClassName($originalClass); | |
eval($this->generateMockClassCode($mockClassName, $originalClass)); | |
return new $mockClassName($this); | |
} | |
public function mockMethod(string $methodName, Closure $closure): self | |
{ | |
$this->methodMocks[$methodName] = $closure; | |
return $this; | |
} | |
/** | |
* @noinspection PhpUnused | |
* @param string $methodName | |
* @return bool | |
*/ | |
public function hasMethodMock(string $methodName): bool | |
{ | |
return isset($this->methodMocks[$methodName]); | |
} | |
/** | |
* @noinspection PhpUnused | |
* @param string $methodName | |
* @param array $args | |
* @return mixed | |
*/ | |
public function callMockedMethod(string $methodName, array $args): mixed | |
{ | |
return call_user_func_array($this->methodMocks[$methodName], $args); | |
} | |
private function generateUniqueClassName(string $originalClass): string | |
{ | |
$originalClass = str_replace('\\', '', $originalClass); | |
return ucfirst($originalClass) . 'Mock' . uniqid(); | |
} | |
private function generateMockClassCode(string $mockClassName, string $originalClass): string | |
{ | |
$reflection = new ReflectionClass($originalClass); | |
$mockCode = "class $mockClassName extends $originalClass {\n"; | |
$mockCode .= "private MockBuilder \$mockBuilder;\n"; | |
$mockCode .= "public function __construct(MockBuilder \$mockBuilder) { \$this->mockBuilder = \$mockBuilder; }\n"; | |
foreach ($reflection->getMethods() as $method) { | |
$methodName = $method->getName(); | |
$returnType = $method->getReturnType()?->getName() ?? ''; | |
$returnTypeDeclaration = $returnType ? ": $returnType" : ''; | |
$parameters = []; | |
$argumentList = []; | |
foreach ($method->getParameters() as $param) { | |
$type = $param->hasType() ? $param->getType()?->getName() . ' ' : ''; | |
$name = '$' . $param->getName(); | |
$parameters[] = $type . $name; | |
$argumentList[] = $name; | |
} | |
$paramList = implode(', ', $parameters); | |
$argNames = implode(', ', $argumentList); | |
$mockCode .= "public function $methodName($paramList)$returnTypeDeclaration {\n"; | |
$mockCode .= " if (\$this->mockBuilder->hasMethodMock('$methodName')) {\n"; | |
$mockCode .= " return \$this->mockBuilder->callMockedMethod('$methodName', [$argNames]);\n"; | |
$mockCode .= " }\n"; | |
$mockCode .= " return parent::$methodName($argNames);\n"; | |
$mockCode .= "}\n"; | |
} | |
$mockCode .= "}"; | |
return $mockCode; | |
} | |
} | |
class Service | |
{ | |
public function getName(): string | |
{ | |
return 'service'; | |
} | |
public function getId(): int | |
{ | |
return 123; | |
} | |
public function find(int $id): array | |
{ | |
return ['id' => $id]; | |
} | |
} | |
class Test | |
{ | |
public function assertEquals(mixed $expected, mixed $actual): void | |
{ | |
if ($expected === $actual) { | |
echo "[✅ PASSED]\n"; | |
} else { | |
echo "[❌ FAILED]: Expected " . json_encode($expected) . " but got " . json_encode($actual) . "\n"; | |
} | |
} | |
public function assertInstanceOf(mixed $actual, string $expectedClass): void | |
{ | |
if ($actual instanceof $expectedClass) { | |
echo "[✅ PASSED]\n"; | |
} else { | |
echo "[❌ FAILED]: Expected instance of $expectedClass but got " . (is_object($actual) ? get_class($actual) : gettype($actual)) . "\n"; | |
} | |
} | |
} | |
function test(string $testName, Closure $testFunction): void | |
{ | |
echo "[🔍 Running Test: $testName]\n"; | |
$testFunction(new Test()); | |
echo "[✔️ Test Completed: $testName]\n\n"; | |
} | |
$mockBuilder = new MockBuilder(); | |
$mockBuilder | |
->mockMethod('getName', fn() => 'service from mock') | |
->mockMethod('find', fn(int $id) => ['id' => $id]); | |
$serviceMock = $mockBuilder->getInstance(Service::class); | |
test('instanceof_service', static function (Test $test) use ($serviceMock) { | |
$test->assertInstanceOf($serviceMock, Service::class); | |
}); | |
test('get_name', static function (Test $test) use ($serviceMock) { | |
$test->assertEquals('service from mock', $serviceMock->getName()); | |
}); | |
test('find', static function (Test $test) use ($serviceMock) { | |
$test->assertEquals(['id' => 123], $serviceMock->find(123)); | |
}); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment