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

Inline Laravel Package

testo/inline

Inline test plugin for Testo: mark methods as tests via PHP attributes, without separate test classes. Ideal for quick checks near production code and self-documenting examples. Install with composer require --dev testo/inline.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the package in your Laravel project:
    composer require --dev testo/inline
    
  2. Ensure Testo is installed (required dependency):
    composer require --dev testo/testo
    
  3. Configure Testo by creating a testo.php config file in your project root (mirror Laravel’s phpunit.xml structure where possible):
    return [
        'bootstrap' => [
            __DIR__.'/tests/bootstrap.php',
        ],
        'suites' => [
            'default' => [
                'paths' => [__DIR__.'/tests'],
                'plugins' => ['testo/inline'],
            ],
        ],
    ];
    
  4. Write your first inline test:
    // app/Services/UserService.php
    use Testo\Inline\Test;
    
    class UserService {
        #[Test]
        public function find_by_id_returns_user() {
            $user = $this->findById(1);
            $this->assertInstanceOf(User::class, $user);
        }
    
        public function findById(int $id) { /* ... */ }
    }
    
  5. Run tests via:
    vendor/bin/testo
    

First Use Case: Quick Validation

Use inline tests for self-contained assertions near production code, such as:

  • Validating a helper function’s output.
  • Checking edge cases in a value object.
  • Documenting expected behavior with executable examples.

Example:

// app/Helpers/StringHelper.php
#[Test]
public function truncate_removes_excess_chars() {
    $result = StringHelper::truncate("Hello World", 5);
    $this->assertEquals("Hello...", $result);
}

Implementation Patterns

Workflow: Inline Testing in Laravel

  1. Colocate Tests: Place inline tests in the same file as the production code they test (e.g., User.php).

    // app/Models/User.php
    #[Test]
    public function email_is_valid() {
        $user = new User(['email' => 'test@example.com']);
        $this->assertTrue(Str::isValidEmail($user->email));
    }
    
  2. Group Tests with TestSuite: Register inline test classes in a TestSuite for organized execution:

    // tests/TestSuite/InlineSuite.php
    use Testo\TestSuite;
    
    return new TestSuite([
        User::class,
        UserService::class,
    ]);
    
  3. Leverage Testo’s Assertions: Use Testo’s assertion methods (similar to PHPUnit but lighter):

    #[Test]
    public function calculate_total_returns_float() {
        $total = $this->calculateTotal([10, 20, 30]);
        $this->assertFloat($total, 60.0);
    }
    
  4. Hybrid Testing: Combine inline tests with Laravel’s PHPUnit tests:

    • Use Testo/inline for domain logic.
    • Use PHPUnit for HTTP/database tests.
    # Run all tests
    composer test
    
    // composer.json
    {
        "scripts": {
            "test": "phpunit && vendor/bin/testo",
            "test:unit": "vendor/bin/testo",
            "test:feature": "phpunit"
        }
    }
    

Integration Tips

  • Avoid Stateful Tests: Inline tests share the same scope as production code. Avoid:

    • Static properties.
    • Global state (e.g., Laravel’s app() bindings).
    • Database queries without transactions.
  • Mocking Dependencies: Manually inject mocks or use Testo’s Mock plugin (if available):

    #[Test]
    public function create_user_calls_repository() {
        $mockRepo = $this->mock(Repository::class);
        $mockRepo->shouldReceive('create')->once();
    
        $this->createUser(['name' => 'John']);
    }
    
  • Test Discovery: Ensure your testo.php config includes the correct paths:

    'suites' => [
        'default' => [
            'paths' => [__DIR__.'/app', __DIR__.'/tests'], // Include app/ for inline tests
        ],
    ],
    
  • CI/CD Pipeline: Add Testo to your CI workflow (e.g., GitHub Actions):

    - name: Run Testo
      run: vendor/bin/testo
    

Gotchas and Tips

Pitfalls

  1. Test Discovery Issues:

    • Problem: Inline tests may not run if their classes aren’t registered in a TestSuite.
    • Fix: Explicitly list all classes with inline tests in testo.php:
      'suites' => [
          'default' => [
              'classes' => [User::class, UserService::class], // Manual registration
          ],
      ],
      
  2. Shared State:

    • Problem: Inline tests execute in the same scope as production code, risking side effects (e.g., modified static properties).
    • Fix: Reset state between tests or use a fresh instance:
      #[Test]
      public function test_with_fresh_instance() {
          $service = new UserService(); // New instance per test
          $this->assertEmpty($service->getCache());
      }
      
  3. Laravel-Specific Gaps:

    • Problem: Missing Laravel helpers (e.g., refreshDatabase(), actingAs()).
    • Fix: Manually handle dependencies or stick to PHPUnit for Laravel-specific tests.
  4. Attribute Conflicts:

    • Problem: PHP 8.1+ attributes may conflict with other tools (e.g., IDE plugins).
    • Fix: Ensure your IDE supports Testo’s attributes (e.g., PHPStorm may need manual test discovery).
  5. Performance Overhead:

    • Problem: Testo’s runtime may be slower than PHPUnit for large suites.
    • Fix: Benchmark and optimize by grouping tests logically in TestSuite.

Debugging Tips

  • Verbose Output: Run tests with -v for detailed output:
    vendor/bin/testo -v
    
  • Isolation: Use #[Before] and #[After] hooks to reset state:
    #[Before]
    public function reset_state() {
        Cache::clear();
    }
    
  • Stack Traces: Testo’s error messages may differ from Laravel’s. Use try-catch for granular debugging:
    #[Test]
    public function debug_failing_test() {
        try {
            $this->expectException(InvalidArgumentException::class);
            $this->doSomethingRisky();
        } catch (Exception $e) {
            $this->assertStringContainsString('Expected error', $e->getMessage());
        }
    }
    

Extension Points

  1. Custom Assertions: Extend Testo’s assertion system by creating a custom plugin:

    // app/Plugins/CustomAssertions.php
    use Testo\Plugin;
    
    class CustomAssertions extends Plugin {
        public function assertJsonEquals($expected, $actual) {
            $this->assertEquals(json_encode($expected, JSON_PRETTY_PRINT), json_encode($actual, JSON_PRETTY_PRINT));
        }
    }
    

    Register in testo.php:

    'plugins' => ['app/Plugins/CustomAssertions'],
    
  2. Test Lifecycle Hooks: Use #[Before] and #[After] for setup/teardown:

    #[Before]
    public function setup_database() {
        DB::connection()->transaction(function () {
            // Setup test data
        });
    }
    
  3. Parallel Testing: If Testo supports parallelization, configure it in testo.php:

    'parallel' => [
        'workers' => 4,
    ],
    

Laravel-Specific Quirks

  • Service Container: Avoid relying on Laravel’s service container in inline tests. Manually resolve dependencies:
    #[Test]
    public function test_with_manual_injection() {
        $repo = new UserRepository();
        $service = new UserService($repo);
        // Test logic
    }
    
  • Artisan Commands: Testo lacks Laravel’s artisan test command. Use custom scripts:
    # package.json
    "scripts": {
        "test:inline": "php vendor/bin/testo"
    }
    
  • Database Testing: Manually manage transactions:
    #[Test]
    public function test_with_transaction() {
        DB::beginTransaction();
        try {
            // Test logic
            DB::commit();
        } catch (Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }
    
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