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

Test Laravel Package

testo/test

Testo plugin that adds the #[Test] attribute and discovery locator. Automatically finds attribute-marked test classes and methods for canonical attribute-driven test detection. Install with composer require --dev testo/test.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package in a Laravel project:

    composer require --dev testo/test
    
  2. Configure Testo (if not already set up):

    • Add testo/testo to your composer.json dev dependencies.
    • Create a testo.php config file in your project root (or extend Testo’s defaults).
    • Example minimal config:
      return [
          'locators' => [
              Testo\Locator\AttributeLocator::class, // Enables #[Test] attribute discovery
          ],
      ];
      
  3. Write your first attribute-driven test:

    use Testo\Test;
    
    #[Test]
    class ExampleTest {
        #[Test]
        public function it_passes(): void {
            assertThat(true)->isTrue();
        }
    
        #[Test]
        private function private_method_can_be_tested(): void {
            assertThat('test')->isNotEmpty();
        }
    }
    
  4. Run tests via:

    ./vendor/bin/testo
    

    Or integrate with Laravel’s Artisan (requires custom command setup).

First Use Case: Migrating from PHPUnit

Replace a PHPUnit test class:

// Before (PHPUnit)
class UserTest extends TestCase {
    public function test_user_can_login() { ... }
}
// After (Testo + #[Test])
#[Test]
class UserTest {
    #[Test]
    public function user_can_login(): void { ... }
}

Implementation Patterns

Core Workflows

1. Test Discovery

  • Default Behavior: The AttributeLocator scans for classes/methods marked with #[Test].
  • Customization: Override locators in testo.php:
    'locators' => [
        Testo\Locator\AttributeLocator::class,
        App\CustomLocator::class, // Additional locators
    ],
    

2. Laravel Integration

  • Service Provider: Register Testo’s test case and commands:
    // app/Providers/TestoServiceProvider.php
    public function register(): void {
        $this->app->bind(TestoTestCase::class, function () {
            return new TestoTestCase();
        });
        $this->commands([
            new TestoCommand(), // Custom Artisan command
        ]);
    }
    
  • TestCase Extension: Extend Testo\TestCase for Laravel-specific setup:
    use Testo\TestCase as BaseTestCase;
    use Illuminate\Foundation\Testing\RefreshDatabase;
    
    class TestCase extends BaseTestCase {
        use RefreshDatabase;
    
        protected function setUp(): void {
            parent::setUp();
            $this->artisan('migrate'); // Laravel-specific setup
        }
    }
    

3. Hybrid Testing

  • Coexist with PHPUnit: Use namespace isolation:
    // Testo tests
    namespace Tests\Testo;
    #[Test]
    class TestoTest { ... }
    
    // PHPUnit tests
    namespace Tests\PHPUnit;
    class PHPUnitTest extends \Tests\TestCase { ... }
    
  • Conditional Execution: Run Testo tests via a custom Artisan command:
    php artisan testo:run
    

4. Private Method Testing

  • Leverage the plugin’s support for private methods:
    #[Test]
    class PrivateMethodTest {
        #[Test]
        private function internal_logic_should_work(): void {
            assertThat($this->internalMethod())->isTrue();
        }
    
        private function internalMethod(): bool { return true; }
    }
    

Integration Tips

  • Assertions: Use Testo’s fluent assertions (e.g., assertThat($value)->isEqualTo($expected)).
  • Fixtures: Integrate with Laravel’s DatabaseMigrations via Testo’s beforeEach/afterEach:
    #[Test]
    class UserTest {
        protected function beforeEach(): void {
            $this->migrate(); // Custom method to run migrations
        }
    }
    
  • Mocking: Use Testo’s mocking plugin (if installed) or integrate with Laravel’s MockFacade:
    use Testo\Mock;
    
    #[Test]
    class MockTest {
        #[Test]
        public function it_mocks_a_service(): void {
            $mock = Mock::mock(Logger::class);
            $mock->shouldReceive('log')->once();
        }
    }
    

Gotchas and Tips

Pitfalls

  1. Attribute Conflicts:

    • Issue: If your project uses PHPUnit’s #[Test] or Pest’s #[Test], conflicts may arise.
    • Fix: Use fully qualified namespaces:
      use Testo\Test;
      #[Test\Test] // Explicit namespace
      
  2. Laravel-Specific Features Missing:

    • Issue: Testo lacks built-in support for Laravel features like RefreshDatabase, QueueTesting, or NotificationTesting.
    • Fix: Create custom traits or extensions:
      trait LaravelRefreshDatabase {
          protected function refreshDatabase(): void {
              $this->artisan('migrate:fresh');
          }
      }
      
  3. Configuration Overrides:

    • Issue: Testo’s config may override Laravel’s testing defaults (e.g., test directory).
    • Fix: Merge configs in testo.php:
      return array_merge(
          require __DIR__.'/../../vendor/testo/testo/config/testo.php',
          [
              'paths' => [
                  'tests' => base_path('tests/Testo'), // Custom test path
              ],
          ]
      );
      
  4. Private Method Limitations:

    • Issue: While the plugin supports private methods, they won’t appear in IDE autocompletion.
    • Fix: Use IDE-specific annotations (e.g., @method) or document tests clearly.
  5. Performance Overhead:

    • Issue: Attribute scanning may add slight overhead compared to naming conventions.
    • Fix: Benchmark and adjust locators if needed:
      'locators' => [
          Testo\Locator\FileLocator::class, // Faster for large suites
          Testo\Locator\AttributeLocator::class,
      ],
      

Debugging

  • Test Not Discovered:

    • Verify the #[Test] attribute is correctly imported (use Testo\Test).
    • Check testo.php for locator configuration.
    • Run with verbose output:
      ./vendor/bin/testo --verbose
      
  • Attribute Errors:

    • Ensure PHP version supports attributes (8.0+).
    • Clear Composer cache if attributes aren’t recognized:
      composer dump-autoload
      
  • Laravel Integration Issues:

    • Check service provider registration.
    • Ensure TestCase extends the correct base class.

Extension Points

  1. Custom Attributes:

    • Create your own attributes by extending Testo\Attribute\Test:
      #[Attribute(Attribute::TARGET_METHOD | Attribute::TARGET_CLASS)]
      class CustomTest extends Test {
          public function __construct(public string $priority = 'medium') {}
      }
      
    • Register a custom locator to handle them.
  2. Locator Extensions:

    • Implement Testo\Locator\LocatorInterface for custom discovery logic:
      class NamespaceLocator implements LocatorInterface {
          public function locate(TestRunner $runner): void {
              $runner->addTestClass(MyTestClass::class);
          }
      }
      
  3. Testo Events:

    • Listen to Testo’s lifecycle events (e.g., TestStarting, TestFailed) via listeners:
      $runner->on(TestStarting::class, function (TestStarting $event) {
          // Custom logic before test runs
      });
      
  4. Laravel Artisan Integration:

    • Extend Testo’s CLI with Laravel commands:
      class TestoCommand extends Command {
          protected $signature = 'testo {--group= : Filter test group}';
          protected $description = 'Run Testo tests';
      
          public function handle(): int {
              $exitCode = (new TestRunner())->run();
              return $exitCode;
          }
      }
      

Tips for Daily Use

  • IDE Support: Configure your IDE (PHPStorm, VSCode) to recognize #[Test] attributes.
  • Test Grouping: Use custom attributes for grouping:
    #[Attribute(Attribute::TARGET_CLASS)]
    class TestGroup {
        public function __construct(public string $group) {}
    }
    
    #[TestGroup('unit')]
    #[Test]
    class UnitTest { ... }
    
  • Legacy Migration: Use a hybrid approach during migration:
    #[Test]
    #[PHPUnit\Test] // Dual annotation for gradual migration
    class HybridTest { ... }
    
  • CI Optimization: Cache Testo’s autoloader in CI for faster runs:
    composer dump-autoload --optimize
    ./vendor/bin/testo
    
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