Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Accessorpair Constraint Laravel Package

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.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package in your Laravel project's test dependencies:

    composer require --dev digitalrevolution/accessorpair-constraint
    
  2. 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;
    }
    
  3. Annotate your test class to cover all public methods of your data class:

    /**
     * @coversDefaultClass \Your\DataClass
     * @covers ::<public>
     */
    
  4. Run the assertion in your test method:

    public function testDataClass()
    {
        static::assertAccessorPairs(YourDataClass::class);
    }
    

First Use Case

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:

  • Verify setName()getName() consistency
  • Verify setAge()getAge() consistency
  • Test default values (if assertPropertyDefaults is enabled)

Implementation Patterns

1. Standard Workflow for DTOs

// TestCase.php
use DigitalRevolution\AccessorPairConstraint\AccessorPairAsserter;

class DtoTest extends TestCase
{
    use AccessorPairAsserter;

    public function testUserDto()
    {
        $this->assertAccessorPairs(UserDto::class);
    }
}

2. Customizing Assertions

Disable constructor testing if it modifies data:

$this->assertAccessorPairs(
    UserDto::class,
    (new ConstraintConfig())->setAssertConstructor(false)
);

3. Excluding Methods

Skip specific methods (e.g., setPasswordHash):

$this->assertAccessorPairs(
    UserDto::class,
    (new ConstraintConfig())->setExcludedMethods(['setPasswordHash'])
);

4. Custom Value Providers

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,
        })
);

5. Inheritance Handling

Test parent class methods explicitly:

// Enable parent method assertions (enabled by default)
$this->assertAccessorPairs(
    ChildDto::class,
    (new ConstraintConfig())->setAssertParentMethods(true)
);

6. Laravel-Specific Integration

Use with Laravel’s HasFactory or API Resources:

// For API Resources (e.g., UserResource)
public function testResource()
{
    $this->assertAccessorPairs(UserResource::class);
}

7. Abstract Test Cases

Extend AbstractDtoTestCase for reusable DTO testing:

use DigitalRevolution\AccessorPairConstraint\AbstractDtoTestCase;

class UserDtoTest extends AbstractDtoTestCase
{
    protected static function getClassToTest(): string
    {
        return UserDto::class;
    }
}

Gotchas and Tips

Pitfalls

  1. Constructor Side Effects

    • If the constructor modifies properties (e.g., strtoupper()), disable assertConstructor:
      (new ConstraintConfig())->setAssertConstructor(false)
      
    • Symptom: Tests fail with mismatched values between constructor and getters.
  2. Final Classes

    • Use setValueProvider to inject mocks:
      ->setValueProvider(fn(string $class) => $class === FinalClass::class
          ? Mockery::mock(FinalClass::class)
          : null)
      
  3. Type Mismatches

    • Ensure getter return types match setter parameter types (e.g., getAge(): intsetAge(int)).
    • Symptom: TypeError during assertion.
  4. Abstract Methods

    • Exclude abstract methods explicitly:
      ->setExcludedMethods(['getAbstractMethod'])
      
  5. PHPUnit Version Conflicts

    • The package supports PHPUnit 11–13. Use ^2.9 for latest features:
      composer require --dev phpunit/phpunit:^11|^12|^13
      

Debugging Tips

  • Verbose Output: Enable PHPUnit’s -v flag to see which methods are being tested.
  • Isolate Failures: Temporarily exclude methods to identify the root cause:
    ->setExcludedMethods(['setProblematicMethod'])
    
  • Check Coverage: Use @covers ::<public> to ensure all methods are tested.

Extension Points

  1. Custom Value Generators Override default providers for domain-specific types:

    ->setValueProvider(fn(string $class) => match($class) {
        Email::class => Email::fromString('test@example.com'),
        // ...
    })
    
  2. Pre/Post-Assertion Hooks Extend AccessorPairAsserter to add setup/teardown:

    protected function beforeAssertAccessorPairs(): void
    {
        // Example: Log test start
    }
    
  3. Integration with Laravel Factories Use factories to seed test data:

    public function testWithFactory()
    {
        $dto = UserDto::fromFactory();
        $this->assertAccessorPairs(UserDto::class, new ConstraintConfig());
    }
    

Performance Considerations

  • Disable assertPropertyDefaults if default values are trivial (e.g., null).
  • Exclude non-critical setters (e.g., setCreatedAt) to speed up tests.

Laravel-Specific Quirks

  • Eloquent Models: Avoid using this for models with database interactions (use feature tests instead).
  • Collections: For Collection-typed properties, ensure the provider returns valid collections:
    ->setValueProvider(fn(string $class) => $class === Collection::class
        ? collect([1, 2, 3])
        : null)
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
emuniq/filament-browser-notifications
syriable/filament-translator
hungnm28/livewire-form
wenprise/eloquent
crudly/encrypted
fadion/bouncy
cuci/prototurk-sdk
gos/pubsub-router-bundle
cuci/prototurk-sdk-symfony
clementtalleu/easyadmin-markdown-bundle
codeflextech/permission-manager
karnoweb/livewire-datepicker
sayedenam/sayed-dashboard
milito/query-filter
apiboxsym/user-bundle
apiboxsym/health-check-bundle
jayeshmepani/jpl-moshier-ephemeris-php
elnasnato/laraliveui
labrodev/rest-sdk
sampaui/sampaui