1. Go to this page and download the library: Download ryunosuke/phpunit-extension library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
ryunosuke / phpunit-extension example snippets
# e.g. bootstrap.php
/**
* @template T
* @param T $actual
* @return \ryunosuke\PHPUnit\Actual|T
*/
function that($actual)
{
return new \ryunosuke\PHPUnit\Actual($actual);
}
// example TestCase
class ActualTest extends \PHPUnit\Framework\TestCase
{
function test_fluent()
{
# fluent interface
// means: assertThat(5, logicalAnd(isType('int'), greaterThanOrEqual(1), lessThanOrEqual(9)));
that(5)->isInt()->isBetween(1, 9);
}
function test_prefixEach()
{
# "each*" asserts per values (assert AND all values)
// means: assertThat(1, greaterThan(0)); assertThat(2, greaterThan(0)); assertThat(3, greaterThan(0));
that([1, 2, 3])->eachGreaterThan(0);
}
function test_suffixAnyAll()
{
# "*Any" asserts multiple arguments (assert OR all arguments)
// means: assertThat('hello world', logicalOr(stringContains('hello'), stringContains('noexists')));
that('hello world')->stringContainsAny(['hello', 'noexists']);
// ignore case (other arguments are normal)
that('hello world')->stringContainsAny(['HELLO', 'noexists'], true);
# "*All" asserts multiple arguments (assert AND all arguments)
// means: assertThat('hello world', logicalAnd(stringContains('hello'), stringContains('world')));
that('hello world')->stringContainsAll(['hello', 'world']);
}
function test_var_use()
{
# "var" returns property of original object (non-public access is possible)
$object = new \ArrayObject(['x' => 'X', 'y' => 'Y'], \ArrayObject::ARRAY_AS_PROPS);
$property = that($object)->var('x');
assertThat($property, equalTo('X'));
# "use" returns method's closure of original object (non-public access is possible)
$object = new \ArrayObject(['x' => 'X', 'y' => 'Y'], \ArrayObject::ARRAY_AS_PROPS);
$method = that($object)->use('getArrayCopy');
assertThat($method(), equalTo(['x' => 'X', 'y' => 'Y']));
}
function test_arrayAccess()
{
# array access returns array's value and actual
$array = ['x' => ['y' => ['z' => [1, 2, 3]]]];
// means: assertThat($array['x']['y']['z'], equalTo([1, 2, 3]));
that($array)['x']['y']['z']->isEqual([1, 2, 3]);
}
function test_propertyAccess()
{
# property access returns property and actual (non-public access is possible)
$object = (object) ['x' => 'X'];
// means: assertThat($object->x, equalTo('X'));
that($object)->x->isEqual('X');
}
function test_methodCall()
{
# method call returns original result and actual (non-public access is possible)
$object = new \ArrayObject([1, 2, 3]);
// means: assertThat($object->getArrayCopy(), equalTo([1, 2, 3]));
that($object)->getArrayCopy()->isEqual([1, 2, 3]);
# actual's method prefers to original method
$object = new \ArrayObject([1, 2, 3]);
// means: assertThat($object, countOf(3)); not: $object->count();
that($object)->count(3);
# "callable" returns original method's callable and actual
that($object)->callable('count')->isCallable();
// "callable"'s arguments mean method arguments
that($object)->callable('setIteratorClass', \stdClass::class)->throws('derived from ArrayIterator');
# "do" invokes original method and actual
that($object)->do('count')->isEqual(3);
# "__invoke" returns original::__invoke and actual
$object = function ($a, $b) { return $a + $b; };
// means: assertThat($object(1, 2), equalTo(3));
that($object)(1, 2)->isEqual(3);
}
function test_methodCallWithBinding()
{
# method call by (...[]) returns method's callable of original object with binding (non-public access is possible)
$closure = function ($arg) { echo $arg; };
that($closure)->callable('__invoke', 'hoge')->outputEquals('hoge');
that($closure)(...['hoge'])->outputEquals('hoge');
}
function test_try()
{
# "try" is not thrown method call and actual
$object = new \ReflectionObject((object) ['x' => 'X']);
// returns original result and actual if not thrown
that($object)->try('getProperty', 'x')->isInstanceOf(\ReflectionProperty::class);
// returns thrown exception and actual if thrown
that($object)->try('getProperty', 'y')->isInstanceOf(\ReflectionException::class);
}
function test_list()
{
# "list" returns reference argument and actual
// means: (fn (&$ref) => $ref = 123)($dummy); assertThat($dummy, equalTo(123));
$dummy = null;
that(fn (&$ref) => $ref = 123)($dummy)->list(0)->isEqual(123);
}
function test_return()
{
# "return" returns original value
$object = new \stdClass();
assertSame($object, that($object)->return());
}
function test_eval()
{
# "eval" asserts directly constraint (variadic arguments OR all arguments)
// means: assertThat('x', equalTo('x'));
that('x')->eval(equalTo('x'));
// means: assertThat('x', logicalOr(equalTo('x'), equalTo('y'), equalTo('z')));
that('x')->eval(equalTo('x'), equalTo('y'), equalTo('z'));
}
function test_as()
{
# "as" describes failure text
// means: assertThat('x', equalTo('notX'), 'this is failure message');
that('x')->as('this is failure message')->isEqual('notX');
}
function test_break()
{
# "break" mark breakable test (converting Failure to Warning)
that('x')->break()->isEqual('notX');
// ...continued this case
}
function test_and_exit()
{
# "and" returns latest actual
$object = new \ArrayObject(['x' => 'abcX', 'y' => 'abcY'], \ArrayObject::ARRAY_AS_PROPS);
// "and" can call as property also as below
that($object)
->x->stringStartsWith('abc')->and->stringLengthEquals(4)->exit()
->y->stringStartsWith('abc')->and->stringLengthEquals(4)->exit()
->getArrayCopy()->count(2)->and->hasKey('x');
# but no need to use them as below
$that = that($object);
$that->getArrayCopy()->count(2)->hasKey('x')->hasKey('y');
$that->x->stringStartsWith('abc')->stringLengthEquals(4);
$that->y->stringStartsWith('abc')->stringLengthEquals(4);
}
function test_declare()
{
# declare is replaced below at runtime
// that(['x', 'y', 'z'])->declare();
that(['x', 'y', 'z'])->is(['x', 'y', 'z']);
}
}
class Example
{
private int $privateField = 0;
public function getPrivate()
{
return $this->privateField;
}
public function setPrivate(int $field)
{
$this->privateField = $field;
}
}
class ExampleTest extends \PHPUnit\Framework\TestCase
{
function test()
{
// test object
$example = that(new Example());
// directry private access
$example->privateField = 3;
$example->privateField->is(3);
// $field is actual
$field = $example->getPrivate();
$field->is(3);
// but, $field can use to arguments
$example->setPrivate($field);
}
}
// Disable. Built-in constraints are not called
\ryunosuke\PHPUnit\Actual::$constraintVariations['isSame'] = false;
// Alias. This ables to use: $actual->isSame('other')
\ryunosuke\PHPUnit\Actual::$constraintVariations['isSame'] = IsIdentical::class;
// Construct. This ables to use: $actual->isArray()
\ryunosuke\PHPUnit\Actual::$constraintVariations['isArray'] = [IsType::class => [IsType::TYPE_ARRAY]];
// Mix. This ables to use: $actual->isNullOrString()
\ryunosuke\PHPUnit\Actual::$constraintVariations['isNullOrString'] = [IsNull::class, IsType::class => [IsType::TYPE_STRING]];
// Instance. This ables to use: $actual->lineCount(5)
\ryunosuke\PHPUnit\Actual::$constraintVariations['lineCount'] = new class(/* argument is used as default */0) extends \PHPUnit\Framework\Constraint\Constraint {
private $lineCount;
public function __construct(int $lineCount)
{
$this->lineCount = $lineCount;
}
protected function matches($other): bool
{
return $this->lineCount === (preg_match_all("#\\R#", $other) + 1);
}
public function toString(): string
{
return 'is ' . $this->lineCount . ' lines';
}
};
// Shorthand instance by closure. This is the same as above
\ryunosuke\PHPUnit\Actual::$constraintVariations['lineCount2'] = function ($other, int $lineCount, string $delimiter = "\\R") {
return $lineCount === (preg_match_all("#$delimiter#", $other) + 1);
};
// This ables to use: $actual->yourConstraint()
\ryunosuke\PHPUnit\Actual::$constraintNamespaces['your\\namespace'] = 'your/constraint/directory';
// Disable. chain case function call
\ryunosuke\PHPUnit\Actual::$functionNamespaces = [];