testo/filter
Testo filtering plugin that selects which tests run by name patterns, file paths, suites, types, and dataset pointers. Powers Testo CLI flags: --filter, --path, --suite, and --type.
Install the Package
Add to composer.json under require-dev:
composer require --dev testo/filter
Ensure your project uses Testo as the test framework (not PHPUnit/Pest).
Basic CLI Usage Run filtered tests via Testo’s CLI flags:
vendor/bin/testo run --filter="UserTest" # Filter by test name
vendor/bin/testo run --path="tests/integration" # Filter by test path
vendor/bin/testo run --suite="auth" # Filter by suite
vendor/bin/testo run --type="unit" # Filter by test type
vendor/bin/testo run --dataset="users:premium" # Filter by dataset
First Use Case: Debugging a Specific Test Replace running the entire suite with:
vendor/bin/testo run --filter="test_login_fails_with_invalid_credentials"
testo/filter source (under plugin/filter/ in the main monorepo) for advanced customization.--help) for available filtering options.composer.json scripts or CI/CD pipelines.
"scripts": {
"test:unit": "vendor/bin/testo run --type=unit",
"test:integration": "vendor/bin/testo run --path=\"tests/integration\"",
"test:debug": "vendor/bin/testo run --filter=\"LoginTest\""
}
- name: Run filtered tests
run: vendor/bin/testo run --path="tests/changed_files"
use Testo\Filter\Filter;
class TestFilterService {
public function apply(string $filterPattern): array {
$filter = new Filter();
return $filter->match($filterPattern); // Returns matched test cases
}
}
AppServiceProvider:
$this->app->singleton(TestFilterService::class, function () {
return new TestFilterService();
});
@dataset("users:premium")).vendor/bin/testo run --dataset="users:premium" --dataset="orders:cancelled"
$filter->matchDataset("users:premium"); // Custom logic to filter by dataset
auth, payments) and run them selectively.testo.php config:
return [
'suites' => [
'auth' => ['tests/unit/Auth/*', 'tests/feature/Login*'],
'payments' => ['tests/unit/Payment*'],
],
];
vendor/bin/testo run --suite="auth"
Artisan Command Wrapper Create a command to bridge Testo’s CLI to Laravel:
php artisan testo:run --filter="UserTest"
// app/Console/Commands/TestoRunCommand.php
public function handle() {
$filter = $this->option('filter');
$command = sprintf('vendor/bin/testo run %s', $filter);
shell_exec($command);
}
Hybrid Test Runner Use Testo for specific test types (e.g., performance) while keeping Laravel tests in PHPUnit:
"scripts": {
"test": "phpunit",
"test:performance": "vendor/bin/testo run --type=performance"
}
Test Case Isolation
TestCase traits (e.g., RefreshDatabase).use Testo\TestCase as BaseTestCase;
class LaravelTestCase extends BaseTestCase {
use RefreshDatabase;
// Laravel-specific traits
}
Parallel Execution: Testo does not natively support parallel testing like PHPUnit. Use CI tools (e.g., GitHub Actions matrix) to split suites:
strategy:
matrix:
suite: [auth, payments, api]
jobs:
test:
run: vendor/bin/testo run --suite="${{ matrix.suite }}"
Cache Filter Results: Store filtered test lists in a config file to avoid reprocessing:
// Cache filtered tests for faster subsequent runs
$filteredTests = cache()->remember('testo.filtered', now()->addHours(1), function () {
return $filter->match($pattern);
});
Framework Lock-In
assertDatabaseHas) will fail.CLI Output Incompatibility
phpunit. Custom parsers may be needed for CI tooling (e.g., Slack notifications).--format=json and post-process output:
vendor/bin/testo run --filter="UserTest" --format=json | jq '.tests'
Dataset Filtering Quirks
@dataset) must match exactly with CLI flags. Typos or missing annotations break filtering.// tests/unit/UserTest.php
public function test_premium_user() {
$this->dataset('users:premium'); // Ensure this matches CLI flag
// ...
}
Path Filtering Edge Cases
--path flags use relative paths from the project root. Incorrect paths (e.g., tests/ vs. ./tests) cause no tests to run.vendor/bin/testo run --path="tests/unit" --list
No Native IDE Support
npm-run-all) to wrap Testo commands.Dry Run Mode
Use --list to preview matched tests without execution:
vendor/bin/testo run --filter="UserTest" --list
Verbose Logging Enable debug output for filter parsing:
vendor/bin/testo run --filter="UserTest" -vv
Testo Configuration
Override default filter behavior in testo.php:
return [
'filter' => [
'strict' => false, // Allow partial name matches
'ignore_case' => true,
],
];
Custom Filter Rules
Extend the Filter class to add logic (e.g., filter by test tags):
namespace App\Extensions;
use Testo\Filter\Filter as BaseFilter;
class TagFilter extends BaseFilter {
public function matchByTag(string $tag): array {
return $this->tests->filter(fn ($test) => str_contains($test->getAnnotation('tag'), $tag));
}
}
Pre-Process Filters Hook into Testo’s event system to modify filters dynamically:
Testo::listen('filtering', function ($event) {
if (app()->environment('local')) {
$event->addFilter('UserTest'); // Auto-add filter in local env
}
});
CI-Specific Filters Use environment variables to dynamically set filters:
FILTER_PATTERN=LoginTest vendor/bin/testo run --filter="$FILTER_PATTERN"
// In a service provider
$filter
How can I help you explore Laravel packages today?