digitalrevolution/accessorpair-constraint
PHPUnit helper to automatically test and cover getters/setters (and constructor-to-getter pairs) on data classes. Add the AccessorPairAsserter trait and call assertAccessorPairs() to validate accessor pairs, optional default/initial value checks.
Install the package in your Laravel project's test dependencies:
composer require --dev digitalrevolution/accessorpair-constraint
Import the trait into your test class or base test case:
use DigitalRevolution\AccessorPairConstraint\AccessorPairAsserter;
use PHPUnit\Framework\TestCase;
class YourTestClass extends TestCase
{
use AccessorPairAsserter;
}
Annotate your test class to cover all public methods of your data class:
/**
* @coversDefaultClass \Your\DataClass
* @covers ::<public>
*/
Run the assertion in your test method:
public function testDataClass()
{
static::assertAccessorPairs(YourDataClass::class);
}
Test a simple DTO (Data Transfer Object) with getters and setters:
class UserDto
{
private string $name;
private int $age;
public function getName(): string { return $this->name; }
public function setName(string $name): self { $this->name = $name; return $this; }
public function getAge(): int { return $this->age; }
public function setAge(int $age): self { $this->age = $age; return $this; }
}
The constraint will automatically:
setName() → getName() consistencysetAge() → getAge() consistencyassertPropertyDefaults is enabled)// TestCase.php
use DigitalRevolution\AccessorPairConstraint\AccessorPairAsserter;
class DtoTest extends TestCase
{
use AccessorPairAsserter;
public function testUserDto()
{
$this->assertAccessorPairs(UserDto::class);
}
}
Disable constructor testing if it modifies data:
$this->assertAccessorPairs(
UserDto::class,
(new ConstraintConfig())->setAssertConstructor(false)
);
Skip specific methods (e.g., setPasswordHash):
$this->assertAccessorPairs(
UserDto::class,
(new ConstraintConfig())->setExcludedMethods(['setPasswordHash'])
);
For complex types (e.g., DateTime or final classes):
$this->assertAccessorPairs(
UserDto::class,
(new ConstraintConfig())
->setValueProvider(fn(string $class) => match($class) {
DateTime::class => new DateTime('now'),
default => null,
})
);
Test parent class methods explicitly:
// Enable parent method assertions (enabled by default)
$this->assertAccessorPairs(
ChildDto::class,
(new ConstraintConfig())->setAssertParentMethods(true)
);
Use with Laravel’s HasFactory or API Resources:
// For API Resources (e.g., UserResource)
public function testResource()
{
$this->assertAccessorPairs(UserResource::class);
}
Extend AbstractDtoTestCase for reusable DTO testing:
use DigitalRevolution\AccessorPairConstraint\AbstractDtoTestCase;
class UserDtoTest extends AbstractDtoTestCase
{
protected static function getClassToTest(): string
{
return UserDto::class;
}
}
Constructor Side Effects
strtoupper()), disable assertConstructor:
(new ConstraintConfig())->setAssertConstructor(false)
Final Classes
setValueProvider to inject mocks:
->setValueProvider(fn(string $class) => $class === FinalClass::class
? Mockery::mock(FinalClass::class)
: null)
Type Mismatches
getAge(): int ↔ setAge(int)).TypeError during assertion.Abstract Methods
->setExcludedMethods(['getAbstractMethod'])
PHPUnit Version Conflicts
^2.9 for latest features:
composer require --dev phpunit/phpunit:^11|^12|^13
-v flag to see which methods are being tested.->setExcludedMethods(['setProblematicMethod'])
@covers ::<public> to ensure all methods are tested.Custom Value Generators Override default providers for domain-specific types:
->setValueProvider(fn(string $class) => match($class) {
Email::class => Email::fromString('test@example.com'),
// ...
})
Pre/Post-Assertion Hooks
Extend AccessorPairAsserter to add setup/teardown:
protected function beforeAssertAccessorPairs(): void
{
// Example: Log test start
}
Integration with Laravel Factories Use factories to seed test data:
public function testWithFactory()
{
$dto = UserDto::fromFactory();
$this->assertAccessorPairs(UserDto::class, new ConstraintConfig());
}
assertPropertyDefaults if default values are trivial (e.g., null).setCreatedAt) to speed up tests.Collection-typed properties, ensure the provider returns valid collections:
->setValueProvider(fn(string $class) => $class === Collection::class
? collect([1, 2, 3])
: null)
How can I help you explore Laravel packages today?