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

Assert Throws Laravel Package

codeception/assert-throws

Adds assertThrows-style assertions to Codeception, letting you easily verify exceptions in tests. Assert the exception type and optionally message/code when running a callable, making negative-path testing clearer and less verbose.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation

    composer require --dev codeception/assert-throws
    

    Ensure your project uses Codeception 4.0+ or PHPUnit 8.0+.

  2. First Use Case Replace a try-catch block or expectException() with:

    use AssertThrows\AssertThrowsTrait;
    
    class UserTest extends \Codeception\Test\Unit
    {
        use AssertThrowsTrait;
    
        public function testInvalidAgeThrowsException()
        {
            $this->assertThrows(
                InvalidArgumentException::class,
                fn() => User::create(['age' => -5])
            );
        }
    }
    
  3. Where to Look First

    • README for syntax and examples.
    • Tests for edge cases (e.g., closures, messages).
    • Laravel-Specific: Check vendor/codeception/assert-throws/src/AssertThrowsTrait.php for extension points.

Implementation Patterns

Core Workflows

  1. Basic Exception Assertion

    $this->assertThrows(
        \InvalidArgumentException::class,
        fn() => $this->userRepository->delete(-1)
    );
    
  2. Assertion with Custom Message

    $this->assertThrows(
        \RuntimeException::class,
        fn() => $this->paymentService->process($invalidPayment),
        'Payment amount must be positive.'
    );
    
  3. Dynamic Exception Classes Useful for polymorphic exceptions (e.g., Laravel’s HttpResponseException):

    $exceptionClass = $this->getExceptionClassForStatus(403);
    $this->assertThrows($exceptionClass, fn() => $this->get('/admin'));
    
  4. Combining with Laravel Testing

    $this->assertThrows(
        \Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException::class,
        fn() => $this->actingAs($user)->post('/admin', ['name' => 'Test'])
    );
    $this->seeResponseCodeIs(401);
    
  5. Negative Assertions Verify no exception is thrown:

    $this->assertDoesNotThrow(
        fn() => $this->userRepository->find(1)
    );
    

Integration Tips

  • Laravel TestCase Integration Extend Laravel\Testing\TestCase and include the trait:

    use Laravel\Testing\TestCase;
    use AssertThrows\AssertThrowsTrait;
    
    class CustomTestCase extends TestCase
    {
        use AssertThrowsTrait;
    }
    
  • Mocking Exceptions Use with Mockery or PHPUnit’s mocks:

    $mock = $this->mock(SomeService::class);
    $mock->shouldThrow(new \RuntimeException('Failed'));
    $this->assertThrows(\RuntimeException::class, fn() => $mock->someMethod());
    
  • Codeception Modules Add to a custom module for reusable assertions:

    namespace Modules;
    
    use AssertThrows\AssertThrowsTrait;
    use Codeception\Module;
    
    class ExceptionModule extends Module
    {
        use AssertThrowsTrait;
    }
    
  • PHPUnit Compatibility If not using Codeception, wrap the trait in a helper:

    function assertThrows($exception, callable $callback, ?string $message = null)
    {
        $trait = new AssertThrowsTrait();
        $trait->assertThrows($exception, $callback, $message);
    }
    

Gotchas and Tips

Pitfalls

  1. Non-Halting Behavior

    • Issue: Unlike expectException(), assertThrows() does not stop test execution if the assertion fails. Subsequent assertions may run, leading to confusing test reports.
    • Fix: Use assertDoesNotThrow() for negative cases or chain assertions carefully:
      $this->assertThrows(\InvalidArgumentException::class, fn() => $this->fail());
      $this->fail('This should not run if the above passes!');
      
  2. Exception Message Matching

    • Issue: Message matching is strict (exact string comparison). Partial matches or regex are not supported.
    • Fix: Use assertStringContainsString() for partial checks:
      try {
          $this->fail();
      } catch (\InvalidArgumentException $e) {
          $this->assertStringContainsString('invalid', $e->getMessage());
      }
      
  3. Closure Scope

    • Issue: The callback closure may not have access to the test’s context (e.g., $this).
    • Fix: Use bindTo() or pass dependencies explicitly:
      $this->assertThrows(
          \RuntimeException::class,
          fn() => $this->service->method(),
          'Error message'
      );
      
  4. Laravel-Specific Exceptions

    • Issue: Laravel’s exceptions (e.g., ValidationException, HttpResponseException) may not extend PHP’s base exceptions, causing false negatives.
    • Fix: Use the full class name or extend the trait to handle Laravel exceptions:
      $this->assertThrows(
          \Illuminate\Validation\ValidationException::class,
          fn() => $this->post('/submit', ['invalid' => 'data'])
      );
      
  5. PHPUnit 12+ Attribute Conflicts

    • Issue: If using PHPUnit’s #[ExpectException], the package may interfere.
    • Fix: Stick to one approach per test file or use #[ExpectException] for halting tests.

Debugging Tips

  1. Verbose Output Run tests with --verbose to see the exact exception details:

    phpunit --verbose
    
  2. Custom Exception Classes For complex exceptions, create a helper:

    function assertCustomException(callable $callback, string $expectedType, array $expectedData = [])
    {
        try {
            $callback();
            $this->fail("Expected {$expectedType} but no exception thrown.");
        } catch (\Throwable $e) {
            $this->assertInstanceOf($expectedType, $e);
            if (!empty($expectedData)) {
                $this->assertEquals($expectedData, $e->getData());
            }
        }
    }
    
  3. Logging Exceptions Temporarily log exceptions to debug:

    $this->assertThrows(
        \RuntimeException::class,
        fn() => $this->riskyOperation(),
        'Expected error'
    );
    // Log the actual exception for debugging
    $this->debug((string) $this->getLastException());
    

Extension Points

  1. Custom Assertion Logic Extend the trait to add Laravel-specific assertions:

    trait LaravelAssertThrows extends AssertThrowsTrait
    {
        public function assertLaravelValidationException(callable $callback, string $field, string $message)
        {
            $this->assertThrows(
                \Illuminate\Validation\ValidationException::class,
                $callback,
                "The {$field} field is required."
            );
        }
    }
    
  2. Global Assertion Overrides Override the trait in a base test case:

    abstract class BaseTestCase extends \Codeception\Test\Unit
    {
        use AssertThrowsTrait {
            assertThrows as protected traitAssertThrows;
        }
    
        protected function assertThrows($exception, callable $callback, ?string $message = null)
        {
            try {
                $this->traitAssertThrows($exception, $callback, $message);
            } catch (\Throwable $e) {
                $this->fail("Assertion failed: " . $e->getMessage());
            }
        }
    }
    
  3. Integration with Laravel’s assertDatabase* Combine with Laravel’s testing helpers:

    $this->assertThrows(
        \Illuminate\Database\QueryException::class,
        fn() => $this->call('POST', '/create', ['invalid' => 'data'])
    );
    $this->assertDatabaseMissing('users', ['email' => 'invalid@example.com']);
    

Configuration Quirks

  1. Codeception Autoloading Ensure the trait is autoloaded in codeception.yml:

    modules:
        enabled:
            - AssertThrows\AssertThrowsTrait
    
  2. PHPUnit Bootstrap If using PHPUnit directly, require the trait in phpunit.xml:

    <php>
        <autoload>
            <classmap>
                <dir>vendor/codeception/assert-throws/src</dir>
            </classmap>
        </autoload>
    </php>
    
  3. IDE Support Some IDEs (e.g., PHPStorm) may not recognize the trait. Add a @mixin annotation:

    /**
     * @mixin AssertThrowsTrait
     */
    class CustomTestCase extends \Codeception\Test
    
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.
besmartand-pro/php-quality-config
sentix/ai-chatbot
terminal42/code-quality-tools
codifyo/ts-generator-bundle
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