Assert
Standalone, lightweight, framework agnostic, test assertion library.
Install / Use
/learn @zenstruck/AssertREADME
zenstruck/assert
This library allows dependency-free test assertions. When using a PHPUnit-based test library (PHPUnit itself, Pest, Codeception), failed assertions are automatically converted to PHPUnit failures and successful assertions are added to PHPUnit's successful assertion count.
This library differs from other popular assertion libraries (webmozart/assert & beberlei/assert) in that it is purely for test assertions opposed to what these libraries provide: type safety assertions.
With the exception of the Expectation API (specifically, the Throws Expectation which provides a nice API for making exception assertions), this library is really only useful for 3rd party libraries that would like to provide test assertions but not have a direct dependency on a specific test library.
Installation
$ composer require zenstruck/assert
Zenstruck\Assert
This is the main entry point for making assertions. When the methods on this class
are called, they throw a Zenstruck\Assert\AssertionFailed on failure. If they
do not throw this exception, they are considered successful.
When using a PHPUnit-based framework, failed assertions are auto-converted to PHPUnit test failures and successful assertions are added to PHPUnit's successful assertion count.
True/False Assertions
use Zenstruck\Assert;
// passes
Assert::true(true === true, 'The condition was not true.');
// fails
Assert::true(true === false, 'The condition was not true.');
// passes
Assert::false(true === false, 'The condition was not false.');
// fails
Assert::false(true === true, 'The condition was not false.');
Generic Fail/Pass
use Zenstruck\Assert;
// trigger a "fail"
Assert::fail('This is a failure.');
// trigger a "pass"
Assert::pass();
Try
Attempt to run a callback and return the result. If an exception is thrown while running, a fail is triggered. If run successfully, a pass is triggered.
use Zenstruck\Assert;
$ret = Assert::try(fn() => 'value'); // $ret === 'value'
Assert::try(fn() => throw new \RuntimeException('exception message')); // "fails" with message "exception message"
// customize the failure message
Assert::try(
fn() => throw new \RuntimeException('exception message'),
'Tried to run the code but {exception} with message "{message}" was thrown.'
); // "fails" with message 'Tried to run the code but RuntimeException with message "exception message" was thrown.'
Run Assertions
Assert::run() executes a callable. A successful execution is considered
a pass and if Zenstruck\Assert\AssertionFailed is thrown, it is a fail.
use Zenstruck\Assert;
use Zenstruck\Assert\AssertionFailed;
// failure
Assert::run(function(): void {
if (true) {
AssertionFailed::throw('This failed.');
}
});
// pass
Assert::run(function(): void {
if (false) {
AssertionFailed::throw('This failed.');
}
});
Expectation API
While the above assertions can be used to create any assertion, a simple, fluent, readable, expectation API is provided. This API is heavily inspired by Pest PHP.
use Zenstruck\Assert;
// empty
Assert::that([])->isEmpty(); // pass
Assert::that(['foo'])->isEmpty(); // fail
Assert::that(null)->isNotEmpty(); // fail
Assert::that('value')->isNotEmpty(); // pass
// null
Assert::that(null)->isNull(); // pass
Assert::that('foo')->isNull(); // fail
Assert::that(null)->isNotNull(); // fail
Assert::that('value')->isNotNull(); // pass
// count
Assert::that([1, 2])->hasCount(2); // pass
Assert::that(new \ArrayIterator([1, 2, 3]))->hasCount(2); // fail
Assert::that(new \ArrayIterator([1, 2]))->doesNotHaveCount(5); // pass
Assert::that($countableObjectWithCountOf5)->doesNotHaveCount(5); // fail
// contains
Assert::that('foobar')->contains('foo'); // pass
Assert::that(['foo', 'bar'])->contains('foo'); // pass
Assert::that('foobar')->contains('baz'); // fail
Assert::that(['foo', 'bar'])->contains(6); // fail
Assert::that('foobar')->doesNotContain('baz'); // pass
Assert::that(new \ArrayIterator(['bar']))->doesNotContain('foo'); // pass
Assert::that('foobar')->doesNotContain('bar'); // fail
Assert::that(['foo', 'bar'])->doesNotContain('bar'); // fail
// array subsets
Assert::that(['foo' => 'bar'])->isSubsetOf(['foo' => 'bar', 'bar' => 'foo']); // pass
Assert::that(['foo' => 'bar'])->isSubsetOf(['bar' => 'foo']); // fail
Assert::that(['foo' => 'bar', 'bar' => 'foo'])->hasSubset(['foo' => 'bar']); // pass
Assert::that(['foo' => 'bar'])->hasSubset(['bar' => 'foo']); // fail
// array subset assertions can also be performed on non-associated arrays (lists/sets).
// Keep in mind that order does not matter.
Assert::that([
'users' => [
['name' => 'user3', 'age' => 20],
['name' => 'user1'],
]
])->isSubsetOf([
'users' => [
['name' => 'user1', 'age' => 25],
['name' => 'user2', 'age' => 23],
['name' => 'user3', 'age' => 20],
]
]); // pass
// also works with json strings that decode to arrays
Assert::that('[3, 1]')->isSubsetOf('[1, 2, 3]'); // pass
// equals (== comparison)
Assert::that('foo')->equals('foo'); // pass
Assert::that('6')->equals(6); // pass
Assert::that('foo')->equals('bar'); // fail
Assert::that(6)->equals(7); // fail
Assert::that('foo')->isNotEqualTo('bar'); // pass
Assert::that(6)->isNotEqualTo('6'); // fail
// is (=== comparison)
Assert::that('foo')->is('foo'); // pass
Assert::that(6)->is(6); // pass
Assert::that('foo')->is('bar'); // fail
Assert::that(6)->is('6'); // fail
Assert::that('foo')->isNot('foo'); // fail
Assert::that(6)->isNot(6); // fail
Assert::that('foo')->isNot('bar'); // pass
Assert::that(6)->isNot('6'); // pass
// boolean (===)
Assert::that(true)->isTrue(); // pass
Assert::that(false)->isTrue(); // fail
Assert::that(true)->isFalse(); // fail
Assert::that(false)->isFalse(); // pass
// boolean (==)
Assert::that(1)->isTruthy(); // pass
Assert::that(new \stdClass())->isTruthy(); // pass
Assert::that('text')->isTruthy(); // pass
Assert::that(null)->isTruthy(); // fail
Assert::that(0)->isFalsy(); // pass
Assert::that(null)->isFalsy(); // pass
Assert::that('')->isFalsy(); // pass
Assert::that(1)->isFalsy(); // fail
// instanceof
Assert::that($object)->isInstanceOf(Some::class);
Assert::that($object)->isNotInstanceOf(Some::class);
// greater than
Assert::that(2)->isGreaterThan(1); // pass
Assert::that(2)->isGreaterThan(1); // fail
Assert::that(2)->isGreaterThan(2); // fail
// greater than or equal to
Assert::that(2)->isGreaterThanOrEqualTo(1); // pass
Assert::that(2)->isGreaterThanOrEqualTo(1); // fail
Assert::that(2)->isGreaterThanOrEqualTo(2); // pass
// less than
Assert::that(3)->isLessThan(4); // pass
Assert::that(3)->isLessThan(2); // fail
Assert::that(3)->isLessThan(3); // fail
// less than or equal to
Assert::that(3)->isLessThanOrEqualTo(4); // pass
Assert::that(3)->isLessThanOrEqualTo(2); // fail
Assert::that(3)->isLessThanOrEqualTo(3); // pass
Type Expectations
use Zenstruck\Assert;
use Zenstruck\Assert\Type;
Assert::that($something)->is(Type::bool());
Assert::that($something)->is(Type::int());
Assert::that($something)->is(Type::float());
Assert::that($something)->is(Type::numeric());
Assert::that($something)->is(Type::string());
Assert::that($something)->is(Type::callable());
Assert::that($something)->is(Type::iterable());
Assert::that($something)->is(Type::countable());
Assert::that($something)->is(Type::object());
Assert::that($something)->is(Type::resource());
Assert::that($something)->is(Type::array());
Assert::that($something)->is(Type::arrayList()); // [1, 2, 3] passes but ['foo' => 'bar'] does not
Assert::that($something)->is(Type::arrayAssoc()); // ['foo' => 'bar'] passes but [1, 2, 3] does not
Assert::that($something)->is(Type::arrayEmpty()); // [] passes but [1, 2, 3] does not
Assert::that($something)->is(Type::json()); // valid json string
// "Not's"
Assert::that($something)->isNot(Type::bool());
Assert::that($something)->isNot(Type::int());
Assert::that($something)->isNot(Type::float());
Assert::that($something)->isNot(Type::numeric());
Assert::that($something)->isNot(Type::string());
Assert::that($something)->isNot(Type::callable());
Assert::that($something)->isNot(Type::iterable());
Assert::that($something)->isNot(Type::countable());
Assert::that($something)->isNot(Type::object());
Assert::that($something)->isNot(Type::resource());
Assert::that($something)->isNot(Type::array());
Assert::that($something)->isNot(Type::arrayList());
Assert::that($something)->isNot(Type::arrayAssoc());
Assert::that($something)->isNot(Type::arrayEmpty());
Assert::that($something)->isNot(Type::json());
Throws Expectation
This expectation provides a nice API for exceptions. It is an alternative to PHPUnit's
expectException() which has the following limitations:
- Can only assert 1 exception is thrown per test.
- Cannot make assertions on the exception itself (other than the message).
- Cannot make post-exception assertions (think side effects).
use Zenstruck\Assert;
// the following can all be used within a single PHPUnit test
// fails if exception not thrown
// fails if exception is thrown but not instance of \RuntimeException
Assert::that(fn() => $code->thatThrowsException())->throws(\RuntimeException::class);
// fails if exception not thrown
// fails if exception is thrown but not instance of \RuntimeException
// fails if exception is thrown but exception message doesn't contain "some message"
Assert::that(fn() => $code->thatThrowsException(
