mirror of
https://github.com/pestphp/pest.git
synced 2026-03-06 07:47:22 +01:00
feat: adds support for global helpers
This commit is contained in:
@ -92,6 +92,8 @@ trait TestCase
|
||||
*/
|
||||
protected function setUp(): void
|
||||
{
|
||||
TestSuite::getInstance()->test = $this;
|
||||
|
||||
parent::setUp();
|
||||
|
||||
$beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename);
|
||||
@ -109,6 +111,8 @@ trait TestCase
|
||||
$this->__callClosure($afterEach, func_get_args());
|
||||
|
||||
parent::tearDown();
|
||||
|
||||
TestSuite::getInstance()->test = null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
83
src/Support/HigherOrderTapProxy.php
Normal file
83
src/Support/HigherOrderTapProxy.php
Normal file
@ -0,0 +1,83 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Pest\Support;
|
||||
|
||||
use ReflectionClass;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class HigherOrderTapProxy
|
||||
{
|
||||
private const UNDEFINED_PROPERTY = 'Undefined property: P\\';
|
||||
|
||||
/**
|
||||
* The target being tapped.
|
||||
*
|
||||
* @var mixed
|
||||
*/
|
||||
public $target;
|
||||
|
||||
/**
|
||||
* Create a new tap proxy instance.
|
||||
*
|
||||
* @param mixed $target
|
||||
*/
|
||||
public function __construct($target)
|
||||
{
|
||||
$this->target = $target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically sets properties on the target.
|
||||
*
|
||||
* @param mixed $value
|
||||
*/
|
||||
public function __set(string $property, $value): void
|
||||
{
|
||||
// @phpstan-ignore-next-line
|
||||
$this->target->{$property} = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically pass properties gets to the target.
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __get(string $property)
|
||||
{
|
||||
try {
|
||||
// @phpstan-ignore-next-line
|
||||
return $this->target->{$property};
|
||||
} catch (\Throwable $throwable) {
|
||||
Reflection::setPropertyValue($throwable, 'file', Backtrace::file());
|
||||
Reflection::setPropertyValue($throwable, 'line', Backtrace::line());
|
||||
|
||||
if (Str::startsWith($message = $throwable->getMessage(), self::UNDEFINED_PROPERTY)) {
|
||||
/** @var \ReflectionClass $reflection */
|
||||
$reflection = (new ReflectionClass($this->target))->getParentClass();
|
||||
Reflection::setPropertyValue($throwable, 'message', sprintf('Undefined property %s::$%s', $reflection->getName(), $property));
|
||||
}
|
||||
|
||||
throw $throwable;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dynamically pass method calls to the target.
|
||||
*
|
||||
* @param array<int, mixed> $arguments
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public function __call(string $methodName, array $arguments)
|
||||
{
|
||||
$filename = Backtrace::file();
|
||||
$line = Backtrace::line();
|
||||
|
||||
return (new HigherOrderMessage($filename, $line, $methodName, $arguments))
|
||||
->call($this->target);
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,13 @@ use Pest\Repositories\TestRepository;
|
||||
*/
|
||||
final class TestSuite
|
||||
{
|
||||
/**
|
||||
* Holds the current test case.
|
||||
*
|
||||
* @var \PHPUnit\Framework\TestCase|null
|
||||
*/
|
||||
public $test;
|
||||
|
||||
/**
|
||||
* Holds the tests repository.
|
||||
*
|
||||
|
||||
@ -8,6 +8,7 @@ use Pest\PendingObjects\BeforeEachCall;
|
||||
use Pest\PendingObjects\TestCall;
|
||||
use Pest\PendingObjects\UsesCall;
|
||||
use Pest\Support\Backtrace;
|
||||
use Pest\Support\HigherOrderTapProxy;
|
||||
use Pest\TestSuite;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
@ -59,8 +60,12 @@ function uses(string ...$classAndTraits): UsesCall
|
||||
*
|
||||
* @return TestCall|TestCase|mixed
|
||||
*/
|
||||
function test(string $description, Closure $closure = null): TestCall
|
||||
function test(string $description = null, Closure $closure = null)
|
||||
{
|
||||
if ($description === null && TestSuite::getInstance()->test) {
|
||||
return new HigherOrderTapProxy(TestSuite::getInstance()->test);
|
||||
}
|
||||
|
||||
$filename = Backtrace::file();
|
||||
|
||||
return new TestCall(TestSuite::getInstance(), $filename, $description, $closure);
|
||||
|
||||
@ -47,6 +47,12 @@
|
||||
✓ it catch exceptions
|
||||
✓ it catch exceptions and messages
|
||||
|
||||
PASS Tests\Features\Helpers
|
||||
✓ it can set/get properties on $this
|
||||
✓ it throws error if property do not exist
|
||||
✓ it allows to call underlying protected/private methods
|
||||
✓ it throws error if method do not exist
|
||||
|
||||
PASS Tests\Features\HigherOrderMessages
|
||||
✓ it proxies calls to object
|
||||
|
||||
@ -126,5 +132,5 @@
|
||||
WARN Tests\Visual\Success
|
||||
s visual snapshot of test suite on success
|
||||
|
||||
Tests: 6 skipped, 65 passed
|
||||
Time: 2.50s
|
||||
Tests: 6 skipped, 69 passed
|
||||
Time: 2.63s
|
||||
|
||||
43
tests/Features/Helpers.php
Normal file
43
tests/Features/Helpers.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
function addName()
|
||||
{
|
||||
test()->user = 'nuno';
|
||||
}
|
||||
|
||||
it('can set/get properties on $this', function () {
|
||||
addName();
|
||||
assertEquals('nuno', $this->name);
|
||||
});
|
||||
|
||||
it('throws error if property do not exist', function () {
|
||||
test()->user;
|
||||
})->throws(\Whoops\Exception\ErrorException::class, 'Undefined property PHPUnit\Framework\TestCase::$name');
|
||||
|
||||
class User
|
||||
{
|
||||
public function getName()
|
||||
{
|
||||
return 'nuno';
|
||||
}
|
||||
}
|
||||
|
||||
function mockUser()
|
||||
{
|
||||
$mock = test()->createMock(User::class);
|
||||
|
||||
$mock->method('getName')
|
||||
->willReturn('maduro');
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
it('allows to call underlying protected/private methods', function () {
|
||||
$user = mockUser();
|
||||
|
||||
assertEquals('maduro', $user->getName());
|
||||
});
|
||||
|
||||
it('throws error if method do not exist', function () {
|
||||
test()->name();
|
||||
})->throws(\ReflectionException::class, 'Call to undefined method PHPUnit\Framework\TestCase::name()');
|
||||
Reference in New Issue
Block a user