Compare commits
3 Commits
Author | SHA1 | Date | |
---|---|---|---|
be4daabc48 | |||
8f21a232b3 | |||
6fef271ec7 |
@ -18,6 +18,7 @@ namespace D3\TestingTools\Tests\Unit\Development;
|
|||||||
use D3\TestingTools\Development\CanAccessRestricted;
|
use D3\TestingTools\Development\CanAccessRestricted;
|
||||||
use D3\TestingTools\Tests\Unit\Development\HelperClasses\CanAccessRestrictedClass;
|
use D3\TestingTools\Tests\Unit\Development\HelperClasses\CanAccessRestrictedClass;
|
||||||
use Error;
|
use Error;
|
||||||
|
use Generator;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use ReflectionException;
|
use ReflectionException;
|
||||||
|
|
||||||
@ -26,7 +27,7 @@ class CanAccessRestrictedTest extends TestCase
|
|||||||
use CanAccessRestricted;
|
use CanAccessRestricted;
|
||||||
|
|
||||||
/** @var CanAccessRestrictedClass */
|
/** @var CanAccessRestrictedClass */
|
||||||
public $class;
|
public CanAccessRestrictedClass $class;
|
||||||
|
|
||||||
public function setUp(): void
|
public function setUp(): void
|
||||||
{
|
{
|
||||||
@ -81,17 +82,12 @@ class CanAccessRestrictedTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function canCallMethodDataProvider(): Generator
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function canCallMethodDataProvider(): array
|
|
||||||
{
|
{
|
||||||
return [
|
yield 'public method' => ['publicMethod', true];
|
||||||
'public method' => ['publicMethod', true],
|
yield 'protected method' => ['protectedMethod', false];
|
||||||
'protected method' => ['protectedMethod', false],
|
yield 'private method' => ['privateMethod', false];
|
||||||
'private method' => ['privateMethod', false],
|
yield 'final public method' => ['finalPublicMethod', true];
|
||||||
'final public method' => ['finalPublicMethod', true],
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -145,16 +141,11 @@ class CanAccessRestrictedTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function canSetAndGetClassPropertiesDataProvider(): Generator
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function canSetAndGetClassPropertiesDataProvider(): array
|
|
||||||
{
|
{
|
||||||
return [
|
yield 'public property' => ['publicProperty', true];
|
||||||
'public property' => ['publicProperty', true],
|
yield 'protected property' => ['protectedProperty', false];
|
||||||
'protected property' => ['protectedProperty', false],
|
yield 'private property' => ['privateProperty', false];
|
||||||
'private property' => ['privateProperty', false],
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -216,16 +207,11 @@ class CanAccessRestrictedTest extends TestCase
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function canSetAndGetMockedPropertiesDataProvider(): Generator
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function canSetAndGetMockedPropertiesDataProvider(): array
|
|
||||||
{
|
{
|
||||||
return [
|
yield 'public property' => ['publicProperty', true];
|
||||||
'public property' => ['publicProperty', true],
|
yield 'protected property' => ['protectedProperty', false];
|
||||||
'protected property' => ['protectedProperty', false],
|
yield 'private property' => ['privateProperty', true]; // because private properties not contained in mock
|
||||||
'private property' => ['privateProperty', true], // because private properties not contained in mock
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,13 +18,13 @@ namespace D3\TestingTools\Tests\Unit\Development\HelperClasses;
|
|||||||
class CanAccessRestrictedClass
|
class CanAccessRestrictedClass
|
||||||
{
|
{
|
||||||
/** @var string */
|
/** @var string */
|
||||||
public $publicProperty = 'publicProperty';
|
public string $publicProperty = 'publicProperty';
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
protected $protectedProperty = 'protectedProperty';
|
protected string $protectedProperty = 'protectedProperty';
|
||||||
|
|
||||||
/** @var string */
|
/** @var string */
|
||||||
private $privateProperty = 'privateProperty';
|
private string $privateProperty = 'privateProperty';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $arg
|
* @param string $arg
|
||||||
|
@ -28,6 +28,10 @@ class IsMockableClass extends IsMockableParent
|
|||||||
*/
|
*/
|
||||||
public function myMethod(string $arg): string
|
public function myMethod(string $arg): string
|
||||||
{
|
{
|
||||||
return 'currentClass::myMethod##.'.$arg;
|
return 'currentClass::myMethod##'.$arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function fakeMethod(): void
|
||||||
|
{
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,12 @@ namespace D3\TestingTools\Tests\Unit\Production;
|
|||||||
use D3\TestingTools\Development\CanAccessRestricted;
|
use D3\TestingTools\Development\CanAccessRestricted;
|
||||||
use D3\TestingTools\Production\IsMockable;
|
use D3\TestingTools\Production\IsMockable;
|
||||||
use D3\TestingTools\Tests\Unit\Production\HelperClasses\IsMockableClass;
|
use D3\TestingTools\Tests\Unit\Production\HelperClasses\IsMockableClass;
|
||||||
|
use D3\TestingTools\Tests\Unit\Production\HelperClasses\IsMockableParent;
|
||||||
|
use Generator;
|
||||||
|
use OxidEsales\Eshop\Application\Model\Article;
|
||||||
use PHPUnit\Framework\MockObject\MockObject;
|
use PHPUnit\Framework\MockObject\MockObject;
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
use ReflectionException;
|
use ReflectionException;
|
||||||
use RuntimeException;
|
|
||||||
|
|
||||||
class IsMockableTest extends TestCase
|
class IsMockableTest extends TestCase
|
||||||
{
|
{
|
||||||
@ -30,44 +32,80 @@ class IsMockableTest extends TestCase
|
|||||||
/**
|
/**
|
||||||
* @test
|
* @test
|
||||||
* @throws ReflectionException
|
* @throws ReflectionException
|
||||||
|
* @covers \D3\TestingTools\Production\IsMockable::d3CallMockableFunction
|
||||||
*/
|
*/
|
||||||
public function callMockableNoParent(): void
|
public function callMockableFunctionMissingFunction(): void
|
||||||
{
|
{
|
||||||
$methodName = $this->getRandomString();
|
$methodName = $this->getRandomString();
|
||||||
$argument = $this->getRandomString();
|
$argument = $this->getRandomString();
|
||||||
|
|
||||||
$traitMock = $this->getObjectForTrait(IsMockable::class);
|
$traitMock = $this->getObjectForTrait(IsMockable::class);
|
||||||
|
|
||||||
$this->expectException(RuntimeException::class);
|
// argument #1 is not a valid callable
|
||||||
|
$this->expectError();
|
||||||
|
|
||||||
$this->callMethod(
|
$this->callMethod(
|
||||||
$traitMock,
|
$traitMock,
|
||||||
'd3CallMockableParent',
|
'd3CallMockableFunction',
|
||||||
[$methodName, [$argument]]
|
[[$traitMock, $methodName], [$argument]]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @test
|
* @param string $fqClassName
|
||||||
|
* @param string $expected
|
||||||
|
*
|
||||||
* @throws ReflectionException
|
* @throws ReflectionException
|
||||||
|
* @test
|
||||||
|
* @dataProvider callMockableFunctionFromClassDataProvider
|
||||||
|
* @covers \D3\TestingTools\Production\IsMockable::d3CallMockableFunction
|
||||||
*/
|
*/
|
||||||
public function callMockableParent(): void
|
public function callMockableFunctionFromClass(string $fqClassName, string $expected): void
|
||||||
{
|
{
|
||||||
$methodName = 'myMethod';
|
$methodName = 'myMethod';
|
||||||
$argument = $this->getRandomString();
|
$argument = $this->getRandomString();
|
||||||
|
|
||||||
/** @var MockObject $mock */
|
/** @var MockObject $mock */
|
||||||
$mock = $this->getMockBuilder(IsMockableClass::class)
|
$mock = new(IsMockableClass::class);
|
||||||
->getMock();
|
|
||||||
// method from mocked class will never call, run method from parent class only
|
if ($fqClassName === 'mockObject') {
|
||||||
$mock->expects($this->never())->method($methodName);
|
$fqClassName = $mock;
|
||||||
|
}
|
||||||
|
|
||||||
$this->assertSame(
|
$this->assertSame(
|
||||||
'ParentClass::myMethod##'.$argument,
|
$expected.$argument,
|
||||||
$this->callMethod(
|
$this->callMethod(
|
||||||
$mock,
|
$mock,
|
||||||
'd3CallMockableParent',
|
'd3CallMockableFunction',
|
||||||
[$methodName, [$argument]]
|
[[$fqClassName, $methodName], [$argument]]
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function callMockableFunctionFromClassDataProvider(): Generator
|
||||||
|
{
|
||||||
|
yield 'parent static method' => [IsMockableParent::class, 'ParentClass::myMethod##'];
|
||||||
|
yield 'current static method' => [IsMockableClass::class, 'currentClass::myMethod##'];
|
||||||
|
yield 'current object method' => ['mockObject', 'currentClass::myMethod##'];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @test
|
||||||
|
* @return void
|
||||||
|
* @throws ReflectionException
|
||||||
|
* @covers \D3\TestingTools\Production\IsMockable::d3GetMockableOxNewObject
|
||||||
|
*/
|
||||||
|
public function canGetMockableOxNewObject()
|
||||||
|
{
|
||||||
|
/** @var MockObject $mock */
|
||||||
|
$mock = new(IsMockableClass::class);
|
||||||
|
|
||||||
|
$this->assertInstanceOf(
|
||||||
|
Article::class,
|
||||||
|
$this->callMethod(
|
||||||
|
$mock,
|
||||||
|
'd3GetMockableOxNewObject',
|
||||||
|
[Article::class]
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
"MIT"
|
"MIT"
|
||||||
],
|
],
|
||||||
"require": {
|
"require": {
|
||||||
"php": "^7.1 || ^8"
|
"php": "^8"
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
"phpunit/phpunit" : "^9.5",
|
"phpunit/phpunit" : "^9.5",
|
||||||
@ -39,8 +39,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"runtests": "./vendor/bin/phpunit",
|
"runtests": "XDEBUG_MODE=coverage ./vendor/bin/phpunit --bootstrap=source/bootstrap.php --config=vendor/d3/testingtools/",
|
||||||
"csfixer": "./vendor/bin/php-cs-fixer fix",
|
"php-cs-fixer_audit": "./vendor/bin/php-cs-fixer list-files --config=./vendor/d3/testingtools/.php-cs-fixer.php",
|
||||||
"phpstan": "./vendor/bin/phpstan analyse src Tests"
|
"php-cs-fixer_fix": "./vendor/bin/php-cs-fixer fix --config=./vendor/d3/testingtools/.php-cs-fixer.php",
|
||||||
|
"phpstan": "./vendor/bin/phpstan analyse -c./vendor/d3/testingtools/phpstan.neon"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,6 +1,12 @@
|
|||||||
parameters:
|
parameters:
|
||||||
|
scanFiles:
|
||||||
|
- ../../oxid-esales/oxideshop-ce/source/bootstrap.php
|
||||||
|
- ../../oxid-esales/oxideshop-ce/source/oxfunctions.php
|
||||||
|
- ../../oxid-esales/oxideshop-ce/source/overridablefunctions.php
|
||||||
|
paths:
|
||||||
|
- .
|
||||||
level: 9
|
level: 9
|
||||||
phpVersion: 70100
|
phpVersion: 80000
|
||||||
checkMissingIterableValueType: false
|
checkMissingIterableValueType: false
|
||||||
ignoreErrors:
|
ignoreErrors:
|
||||||
- '#Property D3\\TestingTools\\Tests\\Unit\\Development\\HelperClasses\\CanAccessRestrictedClass::\$privateProperty is never read, only written.#'
|
- '#Property D3\\TestingTools\\Tests\\Unit\\Development\\HelperClasses\\CanAccessRestrictedClass::\$privateProperty is never read, only written.#'
|
||||||
|
@ -23,32 +23,33 @@ use ReflectionProperty;
|
|||||||
trait CanAccessRestricted
|
trait CanAccessRestricted
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* Calls a private or protected object method.
|
* Calls a public, private or protected object method.
|
||||||
*
|
*
|
||||||
* @param object $object
|
* @param object $object
|
||||||
* @param string $methodName
|
* @param string $methodName
|
||||||
* @param array $arguments
|
* @param array $arguments
|
||||||
*
|
*
|
||||||
* @return mixed
|
* @return mixed
|
||||||
* @throws ReflectionException
|
* @throws ReflectionException
|
||||||
*/
|
*/
|
||||||
public function callMethod($object, string $methodName, array $arguments = [])
|
public function callMethod(object $object, string $methodName, array $arguments = []): mixed
|
||||||
{
|
{
|
||||||
$class = new ReflectionClass($object);
|
$class = new ReflectionClass($object);
|
||||||
$method = $class->getMethod($methodName);
|
$method = $class->getMethod($methodName);
|
||||||
$method->setAccessible(true);
|
$method->setAccessible(true);
|
||||||
|
|
||||||
return $method->invokeArgs($object, $arguments);
|
return $method->invokeArgs($object, $arguments);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a private or protected property in defined class instance
|
* Sets a public, private or protected property in class instance
|
||||||
*
|
*
|
||||||
* @param object $object
|
* @param object $object
|
||||||
* @param string $valueName
|
* @param string $valueName
|
||||||
* @param mixed $value
|
* @param mixed $value
|
||||||
* @throws ReflectionException
|
* @throws ReflectionException
|
||||||
*/
|
*/
|
||||||
public function setValue($object, string $valueName, $value): void
|
public function setValue(object $object, string $valueName, mixed $value): void
|
||||||
{
|
{
|
||||||
$reflection = new ReflectionClass($object);
|
$reflection = new ReflectionClass($object);
|
||||||
$property = $reflection->getProperty($valueName);
|
$property = $reflection->getProperty($valueName);
|
||||||
@ -57,14 +58,14 @@ trait CanAccessRestricted
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* get a private or protected property from defined class instance
|
* get a public, private or protected property from class instance
|
||||||
*
|
*
|
||||||
* @param object $object
|
* @param object $object
|
||||||
* @param string $valueName
|
* @param string $valueName
|
||||||
* @return mixed
|
* @return mixed
|
||||||
* @throws ReflectionException
|
* @throws ReflectionException
|
||||||
*/
|
*/
|
||||||
public function getValue($object, string $valueName)
|
public function getValue(object $object, string $valueName): mixed
|
||||||
{
|
{
|
||||||
$reflection = new ReflectionClass($object);
|
$reflection = new ReflectionClass($object);
|
||||||
$property = $reflection->getProperty($valueName);
|
$property = $reflection->getProperty($valueName);
|
||||||
@ -73,7 +74,7 @@ trait CanAccessRestricted
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets a private or protected property in mocked class instance based on original class
|
* Sets a public, private or protected property in mocked class instance based on original class
|
||||||
* (required for e.g. final properties, which aren't contained in mock, but in original class)
|
* (required for e.g. final properties, which aren't contained in mock, but in original class)
|
||||||
* @param string $mockedClassName * FQNS of original class
|
* @param string $mockedClassName * FQNS of original class
|
||||||
* @param MockObject $object * mock object
|
* @param MockObject $object * mock object
|
||||||
@ -82,7 +83,7 @@ trait CanAccessRestricted
|
|||||||
*
|
*
|
||||||
* @throws ReflectionException
|
* @throws ReflectionException
|
||||||
*/
|
*/
|
||||||
public function setMockedClassValue(string $mockedClassName, MockObject $object, string $valueName, $value): void
|
public function setMockedClassValue(string $mockedClassName, MockObject $object, string $valueName, mixed $value): void
|
||||||
{
|
{
|
||||||
$property = new ReflectionProperty($mockedClassName, $valueName);
|
$property = new ReflectionProperty($mockedClassName, $valueName);
|
||||||
$property->setAccessible(true);
|
$property->setAccessible(true);
|
||||||
@ -100,7 +101,7 @@ trait CanAccessRestricted
|
|||||||
* @return mixed
|
* @return mixed
|
||||||
* @throws ReflectionException
|
* @throws ReflectionException
|
||||||
*/
|
*/
|
||||||
public function getMockedClassValue(string $mockedClassName, MockObject $object, string $valueName)
|
public function getMockedClassValue(string $mockedClassName, MockObject $object, string $valueName): mixed
|
||||||
{
|
{
|
||||||
$property = new ReflectionProperty($mockedClassName, $valueName);
|
$property = new ReflectionProperty($mockedClassName, $valueName);
|
||||||
$property->setAccessible(true);
|
$property->setAccessible(true);
|
||||||
|
@ -15,26 +15,80 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace D3\TestingTools\Production;
|
namespace D3\TestingTools\Production;
|
||||||
|
|
||||||
use RuntimeException as RuntimeExceptionAlias;
|
use OxidEsales\Eshop\Core\Registry;
|
||||||
|
use Psr\Log\LoggerInterface;
|
||||||
|
|
||||||
trait IsMockable
|
trait IsMockable
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
* mockable wrapper for uncertain parent calls
|
* mockable wrapper for uncertain parent calls
|
||||||
*
|
*
|
||||||
* @param string $methodName
|
* @param callable $callable
|
||||||
* @param array $arguments
|
* @param array $arguments
|
||||||
*
|
*
|
||||||
* @return false|mixed
|
* @return false|mixed
|
||||||
*/
|
*/
|
||||||
protected function d3CallMockableParent(string $methodName, array $arguments = [])
|
protected function d3CallMockableFunction(callable $callable, array $arguments = []): mixed
|
||||||
{
|
{
|
||||||
if (get_parent_class($this)) {
|
return call_user_func_array($callable, $arguments);
|
||||||
/** @var callable $callable */
|
}
|
||||||
$callable = [ parent::class, $methodName ];
|
|
||||||
return call_user_func_array($callable, $arguments);
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new RuntimeExceptionAlias('Cannot use "parent" when current class scope has no parent');
|
/**
|
||||||
|
* for mocking use callback:
|
||||||
|
*
|
||||||
|
* $object->method('d3GetMockableOxNewObject')->willReturnCallback(
|
||||||
|
* function () use ($manufacturerMock) {
|
||||||
|
* $args = func_get_args();
|
||||||
|
* switch ($args[0]) {
|
||||||
|
* case Article::class:
|
||||||
|
* return $manufacturerMock;
|
||||||
|
* default:
|
||||||
|
* return call_user_func_array("oxNew", $args);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* );
|
||||||
|
*
|
||||||
|
* @template T
|
||||||
|
* @param class-string<T> $className
|
||||||
|
* @param mixed ...$args constructor arguments
|
||||||
|
*
|
||||||
|
* @return T
|
||||||
|
*/
|
||||||
|
protected function d3GetMockableOxNewObject(string $className, mixed ...$args)
|
||||||
|
{
|
||||||
|
$arguments = func_get_args();
|
||||||
|
return call_user_func_array("oxNew", $arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* for mocking use callback:
|
||||||
|
*
|
||||||
|
* $object->method('d3GetMockableRegistryObject')->willReturnCallback(
|
||||||
|
* function () use ($utilsServerMock) {
|
||||||
|
* $args = func_get_args();
|
||||||
|
* switch ($args[0]) {
|
||||||
|
* case UtilsServer::class:
|
||||||
|
* return $utilsServerMock;
|
||||||
|
* default:
|
||||||
|
* return Registry::get($args[0]);
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* @template T
|
||||||
|
* @param class-string<T> $className
|
||||||
|
*
|
||||||
|
* @return T
|
||||||
|
*/
|
||||||
|
protected function d3GetMockableRegistryObject(string $className)
|
||||||
|
{
|
||||||
|
return Registry::get($className);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return LoggerInterface
|
||||||
|
*/
|
||||||
|
public function d3GetMockableLogger(): LoggerInterface
|
||||||
|
{
|
||||||
|
return Registry::getLogger();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user