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

Tester Laravel Package

draw/tester

Tools for testing PHP apps: DataTester wraps PHPUnit assertions with a fluent, path-based API for arrays/objects. Includes PHPUnit extensions like CarbonReset to reset Carbon state between tests and SetUpAutowire to autowire test properties via attributes.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require draw/tester
    

    Add to composer.json if not auto-loaded:

    "autoload": {
        "psr-4": {
            "Draw\\Component\\Tester\\": "vendor/draw/tester/src"
        }
    }
    

    Run composer dump-autoload.

  2. First Test Case: Create a test class extending PHPUnit\Framework\TestCase and use DataTester for fluent assertions:

    use Draw\Component\Tester\DataTester;
    
    class UserTest extends TestCase {
        public function testUserData() {
            $user = ['name' => 'John', 'age' => 30];
            DataTester::from($user)
                ->assertIsArray()
                ->assertCount(2)
                ->path('name')->assertSame('John')
                ->path('age')->assertGreaterThan(25);
        }
    }
    
  3. PHPUnit Extensions: Enable extensions in phpunit.xml:

    <extensions>
        <bootstrap class="Draw\Component\Tester\PHPUnit\Extension\CarbonReset\CarbonResetExtension"/>
        <bootstrap class="Draw\Component\Tester\PHPUnit\Extension\SetUpAutowire\SetUpAutowireExtension"/>
    </extensions>
    

Implementation Patterns

Data Testing Workflows

  1. Fluent Assertions: Chain assertions for nested data:

    DataTester::from($response)
        ->assertIsArray()
        ->assertCount(1)
        ->path('[0].id')->assertIsInt()
        ->path('[0].name')->assertMatches('/^[A-Z]/');
    
  2. Dynamic Paths: Use variables for dynamic paths:

    $key = 'user';
    DataTester::from($data)
        ->path($key)->assertExists()
        ->path($key . '.email')->assertIsString();
    
  3. Custom Assertions: Extend DataTester for domain-specific rules:

    class UserDataTester extends DataTester {
        public function assertValidEmail() {
            $this->assertMatches('/^[^\s@]+@[^\s@]+\.[^\s@]+$/');
            return $this;
        }
    }
    

PHPUnit Extension Patterns

  1. Carbon Reset: Reset Carbon between tests to avoid timestamp pollution:

    // No manual setup needed; extension handles it globally.
    
  2. Autowiring Services:

    • Mocks: Use #[AutowireMock] for test doubles:
      #[AutowireMock]
      private UserRepository $userRepo;
      
    • Service Replacement: Replace properties in objects:
      #[AutowireMockProperty('userRepo')]
      private UserService $userService;
      
    • Custom Attributes: Create reusable autowire logic (e.g., AutowireRandomInt).
  3. Lifecycle Hooks: Use AutowiredCompletionAwareInterface for post-autowire setup:

    public function postAutowire() {
        $this->userRepo->method('find')->willReturn($mockUser);
    }
    

Integration Tips

  1. Symfony Kernel Tests: Combine with draw/tester-bundle for service autowiring:

    #[AutowireService('app.user_repository')]
    private UserRepository $userRepo;
    
  2. Legacy Code: Use DataTester to validate legacy data structures without modifying assertions.

  3. CI/CD: Add to phpunit.xml for consistent test environments:

    <extensions>
        <bootstrap class="Draw\Component\Tester\PHPUnit\Extension\CarbonReset\CarbonResetExtension"/>
    </extensions>
    

Gotchas and Tips

Pitfalls

  1. Extension Registration:

    • Forgetting to register extensions in phpunit.xml renders them useless.
    • Fix: Verify <extensions> block exists and paths are correct.
  2. Autowire Order:

    • Attributes with higher getPriority() run first. Use this to resolve dependencies.
    • Fix: Set priorities explicitly if conflicts arise.
  3. Static Properties:

    • Autowiring does not work on static properties (documented but easy to overlook).
    • Fix: Use instance properties or manual setup.
  4. CarbonReset Scope:

    • Resets Carbon globally between tests. May affect other test suites if not isolated.
    • Fix: Test Carbon-dependent logic in isolated suites.
  5. DataTester State:

    • Each chain starts fresh; reuse testers carefully:
    // ❌ Avoid (state may leak)
    $tester->assertIsArray()->assertCount(2)->assertIsArray(); // Fails
    
    // ✅ Correct
    DataTester::from($data)->assertIsArray();
    DataTester::from($data)->assertCount(2);
    

Debugging Tips

  1. Autowire Failures:

    • Check for typos in attribute names or property types.
    • Enable PHPUnit’s -vvv for extension debug logs.
  2. Path Assertions:

    • Use assertPathExists() to validate paths before assertions:
    $tester->path('[user.profile]')->assertPathExists()->assertIsArray();
    
  3. Custom Attributes:

    • Test attributes in isolation:
    #[AutowireRandomInt(1, 10)]
    private int $value;
    
    public function testAutowire() {
        $this->assertGreaterThan(0, $this->value);
    }
    

Extension Points

  1. Custom Assertions: Extend DataTester for domain logic:

    class ApiResponseTester extends DataTester {
        public function assertSuccess() {
            $this->assertPathExists('data')->assertPathExists('meta.status')->assertSame('success');
            return $this;
        }
    }
    
  2. Autowire Providers: Create a service provider for complex autowiring (e.g., database fixtures):

    class FixtureAutowire implements AutowireInterface {
        public static function getPriority(): int { return 100; }
        public function autowire(TestCase $testCase, ReflectionProperty $property) {
            $property->setValue($testCase, FixtureFactory::create());
        }
    }
    
  3. CarbonReset Customization: Override the reset logic in a subclass:

    class CustomCarbonResetExtension extends CarbonResetExtension {
        protected function resetCarbon() {
            Carbon::setTestNow(Carbon::now()->addHour());
        }
    }
    

Pro Tips

  • Combine with Laravel: Use DataTester in DatabaseMigrationsTestCase to validate seed data:

    DataTester::from($users)->assertCount(3)->path('[0].email')->assertMatches('/@example\.com/');
    
  • Performance: Cache DataTester instances for large datasets:

    private static $tester;
    public function setUp(): void {
        self::$tester = DataTester::from($this->data);
    }
    
  • Documentation: Add PHPDoc to custom attributes for IDE support:

    #[AutowireRandomInt(1, 10)]
    /** @var int Random value between 1 and 10 */
    private int $randomValue;
    
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.
make-dev/orca
dmstr/symfony-system-resources-bundle
dmstr/symfony-job-queue-bundle
dmstr/openapi-json-schema-bundle
dmstr/keycloak-security-bundle
dmstr/doctrine-audit-log-bundle
dmstr/api-platform-utils-bundle
dmstr/api-configuration-bundle
chrisdev/ux-components
baks-dev/finances
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