Skip to content

Instantly share code, notes, and snippets.

@diloabininyeri
Last active February 18, 2025 02:40
Show Gist options
  • Save diloabininyeri/ec59712b2dddaf3161eb843e400a72ae to your computer and use it in GitHub Desktop.
Save diloabininyeri/ec59712b2dddaf3161eb843e400a72ae to your computer and use it in GitHub Desktop.
builder a mock object and run test with it with the php
<?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