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

Phpunit Service Create Trait Laravel Package

pkly/phpunit-service-create-trait

Laravel/PHP trait that helps you quickly create service instances in PHPUnit tests, reducing boilerplate when setting up dependencies. Handy for service-layer unit tests where you want consistent, reusable test setup.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require --dev pkly/phpunit-service-create-trait
    
  2. Basic Usage Add the trait to your test class and use createRealMockedServiceInstance to instantiate services with auto-mocked dependencies:

    use PKLY\PHPUnit\ServiceMockHelperTrait;
    use PHPUnit\Framework\TestCase;
    
    class MyServiceTest extends TestCase
    {
        use ServiceMockHelperTrait;
    
        private MyService $service;
    
        protected function setUp(): void
        {
            $this->service = $this->createRealMockedServiceInstance(MyService::class);
        }
    
        public function testDeleteSomething()
        {
            $mock = $this->createMock(MyEntity::class);
            $this->getMockedService(EntityManagerInterface::class)
                ->expects($this->once())
                ->method('delete')
                ->with($mock);
    
            $this->service->deleteSomething($mock);
        }
    }
    
  3. Key Files

    • Trait Source: vendor/pkly/phpunit-service-create-trait/src/ServiceMockHelperTrait.php
    • Documentation: README.md (minimal but functional).

Implementation Patterns

Core Workflows

  1. Auto-Mocked Dependencies The trait automatically resolves and mocks constructor dependencies and Symfony’s #[Required] attributes:

    // Service with constructor and required method
    class MyService {
        public function __construct(private Repository $repo) {}
        #[Required] public function setLogger(Logger $logger) {}
    }
    
    // Test: Dependencies are auto-mocked
    $service = $this->createRealMockedServiceInstance(MyService::class);
    
  2. Custom Dependency Injection Override auto-mocking for specific dependencies:

    $customRepo = $this->createMock(Repository::class);
    $service = $this->createRealMockedServiceInstance(
        MyService::class,
        ['constructor' => ['repo' => $customRepo]]
    );
    
  3. Partial Mocking Use createRealPartialMockedServiceInstance for partial mocks:

    $partialService = $this->createRealPartialMockedServiceInstance(
        MyService::class,
        ['methods' => ['deleteSomething']]
    );
    

Laravel-Specific Patterns

  1. Testing Service Providers

    $provider = $this->createRealMockedServiceInstance(MyServiceProvider::class);
    $this->assertInstanceOf(MyService::class, $provider->register());
    
  2. Mocking Eloquent Models

    $user = $this->createRealMockedServiceInstance(User::class);
    $user->method('getAttribute')->willReturn('test');
    
  3. Integration with Laravel’s Mockery Combine with Laravel’s testing helpers:

    $mock = Mockery::mock(Repository::class);
    $service = $this->createRealMockedServiceInstance(
        MyService::class,
        ['constructor' => ['repo' => $mock]]
    );
    
  4. Testing Jobs/Commands

    $job = $this->createRealMockedServiceInstance(MyJob::class);
    $job->handle();
    

Advanced Patterns

  1. Dynamic Dependency Resolution Use getMockedService() to access auto-mocked dependencies:

    $mockedRepo = $this->getMockedService(Repository::class);
    $mockedRepo->expects($this->once())->method('find');
    
  2. Trait Composition Extend the trait for custom logic:

    trait CustomServiceTrait {
        use ServiceMockHelperTrait;
    
        protected function createServiceWithDefaults(string $class): object {
            return $this->createRealMockedServiceInstance($class, [
                'constructor' => ['logger' => $this->createMock(Logger::class)]
            ]);
        }
    }
    
  3. Testing Services with Factories

    $factory = $this->createRealMockedServiceInstance(PostFactory::class);
    $factory->method('create')->willReturn(new Post());
    

Gotchas and Tips

Common Pitfalls

  1. Constructor Parameter Order

    • Error: TypeError if dependency array order mismatches constructor.
    • Fix: Explicitly name dependencies in the constructor array:
      $service = $this->createRealMockedServiceInstance(MyService::class, [
          'constructor' => ['repo' => $mockRepo, 'logger' => $mockLogger]
      ]);
      
  2. Non-Mockable Dependencies

    • Error: ReflectionException if a dependency cannot be mocked (e.g., DateTime).
    • Fix: Provide a real instance or exclude it:
      $service = $this->createRealMockedServiceInstance(MyService::class, [
          'constructor' => ['dateTime' => new DateTime()]
      ]);
      
  3. Symfony #[Required] Attributes

    • Error: RuntimeException if a required method is not called.
    • Fix: Ensure all required methods are invoked or excluded:
      $service = $this->createRealMockedServiceInstance(MyService::class, [
          'required' => ['setLogger' => $this->createMock(Logger::class)]
      ]);
      
  4. Circular Dependencies

    • Error: Infinite recursion during mock creation.
    • Fix: Use disableOriginalConstructor() for circular cases:
      $mock = $this->getMockBuilder(CircularDep::class)
          ->disableOriginalConstructor()
          ->getMock();
      

Debugging Tips

  1. Inspect Auto-Mocked Dependencies Use getMockedService() to verify mocks:

    $mock = $this->getMockedService(Repository::class);
    var_dump($mock->getMockedMethods());
    
  2. Enable Strict Mocking Configure PHPUnit to fail on unexpected method calls:

    $this->getMockedService(Repository::class)
        ->expects($this->any())
        ->method('find')
        ->will($this->throwException(new \RuntimeException('Unexpected call')));
    
  3. Log Service Creation Override the trait method for debugging:

    protected function createRealMockedServiceInstance(string $class, array $options = []): object
    {
        \Log::debug("Creating {$class} with options: " . json_encode($options));
        return parent::createRealMockedServiceInstance($class, $options);
    }
    

Extension Points

  1. Custom Mock Factories Extend the trait to support custom mock creation:

    trait CustomMockTrait {
        use ServiceMockHelperTrait;
    
        protected function createCustomMock(string $class): object {
            return $this->getMockBuilder($class)
                ->setMethods(['customMethod'])
                ->getMock();
        }
    }
    
  2. Integration with Laravel’s AppServiceProvider Bind auto-mocked services to the container:

    $this->app->bind(MyService::class, function ($app) {
        return $this->createRealMockedServiceInstance(MyService::class);
    });
    
  3. Support for Non-Constructor Injection Handle setters or other injection methods:

    $service = $this->createRealMockedServiceInstance(MyService::class);
    $service->setDependency($this->createMock(Dependency::class));
    

Configuration Quirks

  • PHPUnit 10+ Requirement: Ensure your phpunit.xml targets PHPUnit 10+:

    <phpunit bootstrap="vendor/autoload.php">
        <extensions>
            <extension class="PKLY\PHPUnit\ServiceMockHelperTrait"/>
        </extensions>
    </phpunit>
    
  • Autoloading: Run composer dump-autoload after installation if autoloading issues arise.

  • Trait Naming Conflict: If using another ServiceMockHelperTrait, rename the use statement:

    use PKLY\PHPUnit\ServiceMockHelperTrait as ServiceMockTrait;
    

Laravel-Specific Gotchas

  1. Eloquent Model Mocking

    • Pitfall: Mocking Eloquent models may break relationships or observers.
    • Fix: Use partial mocks or avoid mocking models entirely in unit tests.
  2. Service Container Binding

    • Pitfall: Conflicts with Laravel’s container bindings if not handled carefully.
    • Fix: Prefer createRealMockedServiceInstance over container bindings in unit tests.
  3. Testing Jobs with Queues

    • Pitfall: Jobs may dispatch events or interact with queues unexpectedly.
    • Fix: Mock the queue connection:
      $queue = $this->createMock(Queue::class);
      $this->app->instance(Queue::class, $queue);
      
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.
daikazu/eloquent-salesforce-objects
unseen-codes/chat
romalytar/yammi-jobs-monitoring-laravel
kisame76/filament-db-table-state
nqxcode/laravel-lucene-search
dpfx/laravel-livewire-wizards
workos/workos-php-laravel
sofa/laravel-global-scope
nawasara/auth-primitives
adhocrat-io/arkhe-main
make-dev/orca-harpoon
itsemon245/lamet
baks-dev/dashboard
amoifr/pickle-panther-bundle
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