feat: adds support for global helpers

This commit is contained in:
Nuno Maduro
2020-05-13 22:51:50 +02:00
parent 2d85842777
commit 6fc55becc8
6 changed files with 151 additions and 3 deletions

View File

@ -92,6 +92,8 @@ trait TestCase
*/ */
protected function setUp(): void protected function setUp(): void
{ {
TestSuite::getInstance()->test = $this;
parent::setUp(); parent::setUp();
$beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename); $beforeEach = TestSuite::getInstance()->beforeEach->get(self::$__filename);
@ -109,6 +111,8 @@ trait TestCase
$this->__callClosure($afterEach, func_get_args()); $this->__callClosure($afterEach, func_get_args());
parent::tearDown(); parent::tearDown();
TestSuite::getInstance()->test = null;
} }
/** /**

View 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);
}
}

View File

@ -16,6 +16,13 @@ use Pest\Repositories\TestRepository;
*/ */
final class TestSuite final class TestSuite
{ {
/**
* Holds the current test case.
*
* @var \PHPUnit\Framework\TestCase|null
*/
public $test;
/** /**
* Holds the tests repository. * Holds the tests repository.
* *

View File

@ -8,6 +8,7 @@ use Pest\PendingObjects\BeforeEachCall;
use Pest\PendingObjects\TestCall; use Pest\PendingObjects\TestCall;
use Pest\PendingObjects\UsesCall; use Pest\PendingObjects\UsesCall;
use Pest\Support\Backtrace; use Pest\Support\Backtrace;
use Pest\Support\HigherOrderTapProxy;
use Pest\TestSuite; use Pest\TestSuite;
use PHPUnit\Framework\TestCase; use PHPUnit\Framework\TestCase;
@ -59,8 +60,12 @@ function uses(string ...$classAndTraits): UsesCall
* *
* @return TestCall|TestCase|mixed * @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(); $filename = Backtrace::file();
return new TestCall(TestSuite::getInstance(), $filename, $description, $closure); return new TestCall(TestSuite::getInstance(), $filename, $description, $closure);

View File

@ -47,6 +47,12 @@
✓ it catch exceptions ✓ it catch exceptions
✓ it catch exceptions and messages ✓ 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 PASS Tests\Features\HigherOrderMessages
✓ it proxies calls to object ✓ it proxies calls to object
@ -126,5 +132,5 @@
WARN Tests\Visual\Success WARN Tests\Visual\Success
s visual snapshot of test suite on success s visual snapshot of test suite on success
Tests: 6 skipped, 65 passed Tests: 6 skipped, 69 passed
Time: 2.50s Time: 2.63s

View 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()');