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

Callable Fake Laravel Package

timacdonald/callable-fake

A tiny PHP testing utility for faking/invoking callables. CallableFake lets you replace closures or invokable objects, record calls and arguments, assert usage, and return configured values—useful for isolating behavior in PHPUnit/Laravel tests.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require --dev timacdonald/callable-fake
    

    Ensure your phpunit version is 10.x, 11.x, or 12.x (check composer.json requirements).

  2. First Use Case: Replace a callable (e.g., a Closure, method, or static call) with a fake to capture invocations and assert behavior. Example:

    use Timacdonald\CallableFake\CallableFake;
    
    // Create a fake for a Closure
    $fake = CallableFake::create(function () { return 'original'; });
    
    // Replace the callable in your code (e.g., via dependency injection or manual swap)
    $result = $fake(); // Captures the call
    
    // Assert the fake was called
    $fake->assertCalled();
    
  3. Key Classes:

    • CallableFake: Core class for faking and capturing calls.
    • CallableFakeResolver: Resolves return values (supports named returns via name()).
    • CallableFakeException: Thrown on assertion failures.
  4. Where to Look First:

    • README for basic usage.
    • Tests for edge cases.
    • Changelog for version-specific features (e.g., PHP 8.2+ support).

Implementation Patterns

Core Workflows

  1. Faking a Callable: Replace a Closure, method, or static call with a CallableFake instance.

    // Fake a Closure
    $fake = CallableFake::create(fn() => 'original');
    $fake('arg1', 'arg2'); // Captures args
    
    // Fake a method call (via dependency injection or mocking)
    $service = new class($fake) {
        public function __construct(private $callback) {}
        public function execute() { return $this->callback(); }
    };
    
  2. Capturing Invocations: Access arguments, return values, and call count:

    $fake->assertCalledWith('arg1', 'arg2'); // Assert args
    $fake->assertCalledTimes(1); // Assert count
    $fake->getArgs(); // Get all captured args
    
  3. Return Value Resolution:

    • Default: Returns null unless configured.
    • Custom returns:
      $fake->resolveWith('custom_return');
      $fake->resolveWith(function () { return time(); }); // Dynamic
      $fake->resolveWithName('success', 'custom_return'); // Named resolver
      
  4. Assertions:

    • Basic:
      $fake->assertCalled();
      $fake->assertNotCalled();
      
    • Advanced:
      $fake->assertCalledWithConsecutive(
          ['arg1'], ['arg2'] // Assert calls in order
      );
      $fake->assertCalledIndex(0, 'arg1'); // Assert specific call index
      

Integration Tips

  1. Dependency Injection: Use the fake as a test double in Laravel’s container:

    $this->app->bind(Closure::class, fn() => $fake);
    

    Or via constructor injection in tests:

    $fake = CallableFake::create(...);
    $service = new Service($fake);
    
  2. Laravel-Specific:

    • Event Listeners: Fake event handlers:
      $fake = CallableFake::create(...);
      event(new MyEvent());
      $fake->assertCalled(); // Verify listener ran
      
    • Middleware: Replace middleware callbacks:
      $fake = CallableFake::create(...);
      $middleware = new class($fake) implements Closure {
          public function __construct(private $callback) {}
          public function __invoke($request) { return $this->callback($request); }
      };
      
  3. Dynamic Fakes: Generate fakes dynamically for complex scenarios:

    $fakes = collect(range(1, 5))->map(fn($i) => CallableFake::create(...));
    
  4. Partial Mocking: Combine with Laravel’s Mockery or PHPUnit’s MockObject for hybrid testing:

    $mock = $this->mock(Service::class);
    $mock->shouldReceive('callback')->andReturnUsing(
        fn() => CallableFake::create(...)
    );
    

Gotchas and Tips

Pitfalls

  1. PHPUnit Version Mismatch:

    • Error: Class 'Timacdonald\CallableFake\CallableFake' not found.
    • Fix: Ensure phpunit/phpunit is 10.x–12.x (see composer.json).
    • Debug: Run composer why-not timacdonald/callable-fake to check constraints.
  2. Argument Capture Order:

    • Gotcha: getArgs() returns arguments in the order they were passed, not by parameter name.
    • Workaround: Use assertCalledWith() with exact args or named resolvers.
  3. Static Method Faking:

    • Gotcha: Directly faking static methods (e.g., Helper::staticMethod()) requires manual binding.
    • Solution: Use a wrapper or Laravel’s app()->bind():
      $fake = CallableFake::create(...);
      $this->app->bindStatic([Helper::class, 'staticMethod'], $fake);
      
  4. Return Value Overrides:

    • Gotcha: If a fake is resolved with a value, it replaces all return values unless using named resolvers.
    • Tip: Use resolveWithName() for conditional returns:
      $fake->resolveWithName('success', 'data');
      $fake->resolveWithName('error', new Exception());
      
  5. Thread Safety:

    • Gotcha: Fakes are not thread-safe (e.g., in Laravel queues or parallel tests).
    • Fix: Reset fakes between tests or use CallableFake::reset().

Debugging Tips

  1. Assertion Failures:

    • Use getArgs() to inspect captured arguments:
      var_dump($fake->getArgs()); // Debug actual calls
      
    • For complex assertions, chain methods:
      $fake->assertCalledTimes(2)->assertCalledWithConsecutive([...], [...]);
      
  2. Lazy Evaluation:

    • Resolvers (e.g., resolveWith()) are lazy—they run only when the fake is invoked.
    • Tip: Use getReturnValue() to inspect the resolved value after invocation.
  3. Laravel-Specific Debugging:

    • Service Container: Clear the container between tests if fakes persist:
      $this->app->forgetInstance(Closure::class);
      
    • Events: Use Events::fake() alongside CallableFake to isolate event testing.

Extension Points

  1. Custom Resolvers: Extend CallableFakeResolver for domain-specific logic:

    class CustomResolver extends CallableFakeResolver {
        public function resolve(): mixed {
            return $this->name === 'api' ? $this->getApiResponse() : null;
        }
    }
    $fake->setResolver(new CustomResolver());
    
  2. Integration with Laravel Packages:

    • Example: Fake a package’s Closure-based callback:
      $fake = CallableFake::create(...);
      $package->setCallback($fake); // Hypothetical package method
      
    • Tip: Check the package’s source for callable hooks (e.g., setCallback, onEvent).
  3. Performance:

    • Optimization: For high-volume tests, reuse fakes instead of recreating them:
      $fake = CallableFake::create(...);
      $this->fake = $fake; // Store for reuse
      $fake->reset(); // Clear state between tests
      
  4. Legacy Code:

    • Workaround: For PHP <8.2 or PHPUnit <10, use an older version via Composer:
      composer require timacdonald/callable-fake:1.7.0
      
    • Note: Drop support for PHP 7.x/8.0 in v1.6.0.
  5. Named Resolvers:

    • Use Case: Simulate different responses (e.g., success/error):
      $fake->resolveWithName('success', ['data' => 'value']);
      $fake->resolveWithName('error', ['error' => 'failed']);
      // Trigger based on args:
      $fake->assertCalledWithName('success', ['arg1']);
      
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.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky