CLI Commands
TestLink provides two ways to run commands: the standalone CLI (testlink) and Pest plugin integration (pest --*).
Standalone CLI (Recommended)
The standalone CLI works with any testing framework (Pest, PHPUnit, or both).
Installation
After installing via Composer, the testlink command is available at:
./vendor/bin/testlinkCommands
Report
Show coverage links from #[TestedBy] attributes:
testlink reportOutput:
Coverage Links Report
─────────────────────
App\Services\UserService
create()
→ Tests\Unit\UserServiceTest::it creates a user
→ Tests\Unit\UserServiceTest::it validates email
update()
→ Tests\Unit\UserServiceTest::it updates a user
Summary
Methods with tests: 2
Total test links: 3Options:
| Option | Description |
|---|---|
--json | Output as JSON |
--path=<dir> | Limit scan to directory |
--framework=<fw> | Filter by framework (pest, phpunit) |
Validate
Verify all coverage links are synchronized and detect unresolved placeholders:
testlink validateSuccess output:
Validation Report
─────────────────
Link Summary
────────────
PHPUnit attribute links: 5
Pest method chain links: 10
Total links: 15
✓ All links are valid!Output with unresolved placeholders:
Validation Report
─────────────────
Unresolved Placeholders
───────────────────────
⚠ @user-create (1 production, 2 tests)
⚠ @A (2 production, 0 tests)
⚠ Run "testlink pair" to resolve placeholders.
Link Summary
────────────
PHPUnit attribute links: 5
Pest method chain links: 10
Total links: 15
✓ All links are valid!Failure output (duplicate links):
Validation Report
─────────────────
Duplicate Links Found
─────────────────────
! Tests\Unit\UserServiceTest::test_creates_user
→ App\Services\UserService::create
⚠ Consider using only one linking method per test.Options:
| Option | Description |
|---|---|
--strict | Fail on warnings (including unresolved placeholders) |
--json | Output as JSON |
--path=<dir> | Limit scan to directory |
Placeholder Detection
The validate command automatically detects unresolved placeholders. In normal mode, this shows a warning but doesn't fail. Use --strict to fail when placeholders are found.
Sync
Synchronize #[TestedBy] attributes to test files:
testlink syncThis reads all #[TestedBy] attributes and adds corresponding links to test files:
- Pest: Adds
->linksAndCovers()method calls - PHPUnit: Adds
#[LinksAndCovers]attributes
Options:
| Option | Description |
|---|---|
--dry-run | Preview changes without applying |
--link-only | Use links() / #[Links] instead of linksAndCovers() / #[LinksAndCovers] |
--prune | Remove orphaned link calls |
--force | Required with --prune for safety |
--path=<dir> | Limit sync to directory |
--framework=<fw> | Target framework (pest, phpunit, auto) |
Examples:
# Preview changes
testlink sync --dry-run
# Apply changes
testlink sync
# Sync with links() only (no coverage)
testlink sync --link-only
# Sync and prune orphans
testlink sync --prune --force
# Sync specific directory
testlink sync --path=src/Services
# Target specific framework
testlink sync --framework=phpunitPair
Resolve placeholder markers (@A, @user-create) into real test-production links:
testlink pairPlaceholders are temporary markers used during rapid TDD/BDD development. Instead of writing full class references, you use short markers that get resolved later.
Output:
Pairing Placeholders
────────────────────
Scanning for placeholders...
Found Placeholders
──────────────────
✓ @user-create 1 production × 2 tests = 2 links
✓ @A 2 production × 3 tests = 6 links
Production Files
────────────────
src/Services/UserService.php
@user-create → UserServiceTest::it creates a user
Test Files
──────────
tests/Unit/UserServiceTest.php
@user-create → UserService::create
✓ Pairing complete. Modified 2 file(s) with 8 change(s).Options:
| Option | Description |
|---|---|
--dry-run | Preview changes without applying |
--placeholder=@X | Resolve only the specified placeholder |
Examples:
# Preview all placeholder resolutions
testlink pair --dry-run
# Apply all placeholder resolutions
testlink pair
# Resolve only a specific placeholder
testlink pair --placeholder=@user-createPlaceholder Syntax
Placeholders must start with @ followed by a letter. Valid examples: @A, @B, @user-create, @MyFeature123.
See the Placeholder Pairing Guide for detailed usage.
Global Options
Available for all commands:
| Option | Description |
|---|---|
--help, -h | Show help |
--version, -v | Show version |
--verbose | Show detailed output |
--no-color | Disable colored output |
Pest Plugin (Alternative)
If you're using Pest, you can also use these commands through the Pest CLI:
Report
pest --coverage-linksReport as JSON
pest --coverage-links-jsonValidate
pest --validate-coverage-linksSync
pest --sync-coverage-links
pest --sync-coverage-links --dry-run
pest --sync-coverage-links --link-only
pest --sync-coverage-links --prune --forceHelp
pest --help-testlinkChoosing Between CLIs
The standalone testlink CLI is recommended because:
- Works with both Pest and PHPUnit
- Framework-agnostic output
- Better for CI/CD pipelines
- Consistent behavior across projects
Exit Codes
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | Validation failed or errors occurred |
CI Usage
GitHub Actions
name: Test
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: '8.3'
- run: composer install
- name: Validate Coverage Links
run: ./vendor/bin/testlink validate --strict
- name: Run Tests
run: ./vendor/bin/pest # or ./vendor/bin/phpunitGitLab CI
test:
stage: test
script:
- composer install
- ./vendor/bin/testlink validate --strict
- ./vendor/bin/pest # or ./vendor/bin/phpunitCircleCI
jobs:
test:
docker:
- image: cimg/php:8.3
steps:
- checkout
- run: composer install
- run: ./vendor/bin/testlink validate --strict
- run: ./vendor/bin/pest # or ./vendor/bin/phpunitJSON Output
Both CLI tools support JSON output for CI/CD integration:
# Standalone CLI
testlink report --json > coverage-links.json
# Pest plugin
pest --coverage-links-json > coverage-links.jsonExample JSON output:
{
"links": {
"App\\Services\\UserService::create": [
"Tests\\Unit\\UserServiceTest::it creates a user",
"Tests\\Unit\\UserServiceTest::it validates email"
]
},
"summary": {
"total_methods": 1,
"total_tests": 2
}
}