This library is built upon webmozart/assert
and provides a fluent interface
for assertions, which allows for a more compact
set of assertions on the same value.
It is a useful tool for those who want to use webmozart/assert
but prefer a more fluent interface.
Built with love ❤️
Use Composer to install the library.
composer require exeque/fluent-assert
There are a few differences between this library and webmozart/assert
:
- The
all*
methods don't use the source methods inwebmozart/assert
.- They work as you would expect, but they use
each()
internally to apply the assertions to each value. See Iterative Assertions.
- They work as you would expect, but they use
- The exception throw on failures are different.
\ExeQue\FluentAssert\Exceptions\InvalidArgumentException
is thrown on failures.- It extends
\Webmozart\Assert\InvalidArgumentException
.
- It extends
\ExeQue\FluentAssert\Exceptions\BulkInvalidArgumentException
is thrown on grouped assertions- It extends
\ExeQue\FluentAssert\Exceptions\InvalidArgumentException
. - It provides multiple exceptions in the
getExceptions()
method. - See Grouped Assertions.
- It extends
\ExeQue\FluentAssert\Exceptions\IndexedInvalidArgumentException
is thrown on positional or iterative assertions- It extends
\ExeQue\FluentAssert\Exceptions\InvalidArgumentException
. - It provides the index of the failing assertion in the
getIndex()
method. - It provides the original message in the
getOriginalMessage()
method. - See Positional Assertions or Iterative Assertions.
- It extends
use ExeQue\FluentAssert\Assert;
$assert = Assert::for('foo bar');
// All methods from webmozart/assert are available
// The arguments are the same except for the first one (the value to assert).
$assert
->string() // Webmozart/Assert::string(..., $message = '')
->startsWith('foo') // Webmozart/Assert::startsWith(..., $prefix, $message = '')
->endsWith('bar'); // Webmozart/Assert::endsWith(..., $suffix, $message = '')
// You can retrieve via the `value()` method.
$value = $assert->value(); // 'foo bar'
The example from webmozart/assert
can be rewritten as:
use ExeQue\FluentAssert\Assert;
class Employee
{
public function __construct($id)
{
Assert::for($id)
->integer('The employee ID must be an integer. Got: %s')
->greaterThan(0, 'The employee ID must be a positive integer. Got: %s');
}
}
new Employee('foo bar');
// -> ExeQue\FluentAssert\Exceptions\InvalidArgumentException:
// The employee ID must be an integer. Got: string
new Employee(-10);
// -> ExeQue\FluentAssert\Exceptions\InvalidArgumentException:
// The employee ID must be a positive integer. Got: -10
The fluent interface provides a and()
and or()
method to group assertions.
This is useful when you want to make multiple assertions at once and a singular combined error.
use ExeQue\FluentAssert\Assert;
$assert = Assert::for('fizz buzz');
// Using `and()` will throw an exception if any of the assertions fail.
// The errors will be combined into a single exception.
$assert->and(
fn (Assert $assert) => $assert->startsWith('foo'),
fn (Assert $assert) => $assert->endsWith('bar'),
);
// -> ExeQue\FluentAssert\Exceptions\BulkInvalidArgumentException:
// Expected a value to start with "foo" (Got: "fizz buzz"), or expected a value to end with "bar" (Got: "fizz buzz").
// Using `or()` will only throw an exception if all the assertions fail.
// The errors will be combined into a single exception.
$assert->or(
fn (Assert $assert) => $assert->startsWith('foo'),
fn (Assert $assert) => $assert->startsWith('fizz'),
):
// -> Does not fail
$assert->or(
fn (Assert $assert) => $assert->startsWith('foo'),
fn (Assert $assert) => $assert->startsWith('bar'),
):
// -> ExeQue\FluentAssert\Exceptions\BulkInvalidArgumentException:
// Expected a value to start with "foo" (Got: "fizz buzz"), or expected a value to start with "bar" (Got: "fizz buzz").
The each()
method allows you to iterate over an array or an ArrayAccess
object and apply assertions to each value.
The errors thrown will have a prefix of failing index applied.
use ExeQue\FluentAssert\Assert;
$assert = Assert::for(['foo', 'bar', 'baz']);
$assert->each(
fn (Assert $assert) => $assert->string()->length(3)
);
$assert->each(
fn (Assert $assert) => $assert->inArray(['foo', 'bar'])
);
// -> ExeQue\FluentAssert\Exceptions\IndexedInvalidArgumentException:
// [2]: Expected one of: "foo", "bar". Got: "baz"
The IndexedInvalidArgumentException
can provide the index that failed use getIndex()
and the original message using
getOriginalMessage()
.
An alternative version eachKey
is available to apply assertions to the keys of an iterable.
### Positional Assertions
The `at()` method allows you to assert a value at a specific index in an array or an `ArrayAccess` object.
It automatically calls `keyExists()` on the index before applying the assertion.
The errors thrown will have a prefix of failing index applied.
```php
use ExeQue\FluentAssert\Assert;
// Works with integer indices
$assert = Assert::for(['foo', 'bar', 'baz']);
$assert->at(0, fn (Assert $assert) => $assert->eq('foo'));
// Works with string indices
$assert = Assert::for(['foo' => 'bar', 'baz' => 'qux']);
$assert->at('baz', fn (Assert $assert) => $assert->eq('qux'));
$assert = Assert::for(['foo' => 'bar']);
$assert->at('foo', fn (Assert $assert) => $assert->eq('fizz'));
// -> ExeQue\FluentAssert\Exceptions\IndexedInvalidArgumentException:
// [foo]: Expected a value equal to "fizz". Got: "bar"
Sometimes you only want to apply certain assertions if a condition is met or not. The when()
method allows you to do
this.
use ExeQue\FluentAssert\Assert;
use ExeQue\FluentAssert\ConditionAssert;
$assert = Assert::for(['foo', 'bar', 'baz']);
// The `when()` method takes a callable that may return a boolean value
// or a `ConditionAssert` object.
$assert->when(
// Any truthy value will be considered true
condition: fn (Assert $assert) => true,
// [Required] Assertions to apply if the condition is true
then: fn (Assert $assert) => null,
// [Optional] Assertions to apply if the condition is false
otherwise: fn (Assert $assert) => null,
);
// If the condition uses the `Assert` provided as an argument and
// returns null (or nothing) and the inner assertion did not fail,
// then that is considered true.
$assert = Assert::for('foo bar');
$assert->when(
function (Assert $assert) {
$assert->string(); // Returns nothing
},
fn(Assert $assert) => null, // Is called
);
$assert->when(
function (Assert $assert) {
$assert->string();
return null;
},
fn(Assert $assert) => null, // Is called
);
$assert->when(
function (Assert $assert) {
$assert->isArray();
},
fn(Assert $assert) => null, // Is not called
fn(Assert $assert) => null, // Is called
);
The not()
method allows you to invert the assertion. This is useful when the assertions MUST fail.
use ExeQue\FluentAssert\Assert;
$assert = Assert::for([1, 2, 3, 4]);
$assert->not(
fn (Assert $assert) => $assert->arrayContains(3),
'Input cannot contain the number 3'
);
All assertions from webmozart/assert
are available. See here
An extended implementation of webmozart/assert
is available in the ExeQue\FluentAssert\Base
class.
The following additional assertions are available:
Method | Description |
---|---|
hasIndices($value, string $message = '') |
Check that a value has indices (array or ArrayAccess) |
arrayContains($array, mixed $value, string $message = '') |
Check that a value contains another value |
type($value, string $type, string $message = '') |
Check that a value is of a certain type (using get_debug_type() ) |
publicPropertyExists($value, string $name, string $message = '') |
Check that a value has a public property |
protectedPropertyExists($value, string $name, string $message = '') |
Check that a value has a protected property |
privatePropertyExists($value, string $name, string $message = '') |
Check that a value has a private property |
staticPropertyExists($value, string $name, string $message = '') |
Check that a value has a static property |
instancedPropertyExists($value, string $name, string $message = '') |
Check that a value has an instanced property |
enumCaseExists($value, string $enumClass, string $message = '') |
Check that a value is a case in an enum |
enumValueExists($value, string $enumClass, string $message = '') |
Check that a value is a value in an enum |
nullOr($value, callable $callback) |
Execute the callback if the value is not null |
The library uses code generation to create definitions for all methods present in webmozart/assert
.
To trigger the generation run:
composer build
You can run the tests using the following command:
composer test
This library is open-sourced software licensed under the MIT license.
Big thanks to Bernard Schussek (and community) for the webmozart/assert
.
It is one of the first dependencies I include in any project and I use it extensively.