testo/assert
Assertion plugin for the Testo PHP testing framework. Adds a fluent assert/expect facade, expectation lifecycle, and helpers for matching thrown exceptions. Reports comparisons through Testo’s standard pipeline. Install via Composer: testo/assert.
composer require --dev testo/assert
use Testo\Assert\Assert;
Assert::that($actualValue)->equals($expectedValue);
0.1.4.md) for recent features like notNull() or ComparisonFailure.Replace a basic PHPUnit assertion with a fluent Testo assertion:
// Before (PHPUnit)
$this->assertArrayHasKey('data', $response);
$this->assertNotEmpty($response['data']);
// After (Testo)
Assert::that($response)
->arrayHasKey('data')
->isNotEmpty();
Fluent Assertions Chain methods for nested validations:
Assert::that($user)
->isInstanceOf(User::class)
->hasAttribute('email', 'user@example.com')
->hasRole('admin');
Exception Matching Assert exceptions with custom messages or types:
Assert::that(fn() => $this->invalidOperation())
->throws(ValidationException::class)
->withMessage('The email field is required.');
Collection Assertions Validate arrays/objects with diffs on failure:
Assert::that($posts)
->isArray()
->hasCount(3)
->allMatch(fn($post) => Assert::that($post)->hasKey('title'));
Lazy Assertions Defer evaluation until test failure (useful for setup/teardown):
$lazyAssert = Assert::lazy($user)->isActive();
// ... later in test ...
$lazyAssert->assert();
Assert::describe() to group related assertions for better reporting:
Assert::describe('User Validation', function() {
Assert::that($user)->isValid();
Assert::that($user->roles)->contains('admin');
});
function assertResponseHasData($response) {
Assert::that($response->json())
->arrayHasKey('data')
->isNotEmpty();
}
Assert by adding static methods for domain-specific checks:
Assert::static('hasPermission', function($user, $permission) {
return $user->permissions()->contains($permission);
});
testo.php config includes:
'plugins' => [
Testo\Assert\Assert::class,
],
Framework Lock-In
Assertion Exception Handling
AssertionException and ComparisonFailure (not PHPUnit’s AssertionFailedError).try {
Assert::that($user)->isActive();
} catch (AssertionException $e) {
$this->fail($e->getMessage());
}
Diff Output Quirks
ComparisonFailure diffs may not handle complex objects (e.g., DateTime, custom classes) intuitively.->toString() or custom __toString() for objects in assertions.IDE Autocompletion
Laravel Test Helpers Conflict
assertDatabaseHas() won’t work with Testo. Use Testo’s native DB assertions or create wrappers.@test or try-catch without rethrowing.Assert::that($user->age)->greaterThan(18)
->withMessage('User must be 18+ to access this feature.');
\Log::debug('Asserting on:', ['user' => $user->toArray()]);
Assert::that($user)->isValid();
testo.php:
'plugins' => [
Testo\Assert\Assert::class,
// Other plugins...
],
testo/assert is in composer.json’s autoload-dev:
"autoload-dev": {
"psr-4": {
"Testo\\": "vendor/testo/"
}
}
Custom Assertions
Extend the Assert class to add domain-specific methods:
class DomainAssert extends Assert {
public static function hasPermission($user, $permission) {
return self::that($user->permissions)->contains($permission);
}
}
Exception Matchers
Override throws() behavior for custom exceptions:
Assert::that(fn() => $this->action())
->throws(function($e) {
return $e instanceof \RuntimeException
&& str_contains($e->getMessage(), 'timeout');
});
Testo Event Listeners Hook into Testo’s lifecycle to pre-process assertions (e.g., logging, mocking):
Testo::listening(function($event) {
if ($event instanceof AssertionPassed) {
\Log::debug('Assertion passed:', [$event->assertion]);
}
});
Laravel Test Events
Bridge Testo assertions with Laravel’s test events (e.g., testsPassed):
Testo::listening(function($event) {
if ($event instanceof TestSuiteStarted) {
\Log::info('Running Testo suite with custom assertions...');
}
});
How can I help you explore Laravel packages today?