Sync Command
The testlink sync command synchronizes #[TestedBy] attributes to test links.
Basic Usage
testlink syncOr via Pest:
pest --sync-coverage-linksHow It Works
- Scans production code - Finds all
#[TestedBy]attributes via Composer classmap - Locates test files - Maps test class names to file paths
- Parses test files - Finds test cases using PHP-Parser
- Injects links:
- Pest: Adds
->linksAndCovers()calls before the semicolon - PHPUnit: Adds
#[LinksAndCovers]attributes above the method
- Pest: Adds
Command Output
Normal Mode
$ testlink sync
Scanning production code for #[TestedBy] attributes...
✓ Sync complete!
3 link(s) added
Modified files:
- tests/Unit/UserServiceTest.php (2 links added)
- tests/Unit/OrderServiceTest.php (1 link added)No Changes Needed
$ testlink sync
Scanning production code for #[TestedBy] attributes...
✓ All coverage links are already in sync.With Errors
$ testlink sync
Scanning production code for #[TestedBy] attributes...
Sync failed with errors:
✗ Test not found: "nonexistent test" in tests/Unit/UserServiceTest.php
✗ File not found: tests/Unit/MissingTest.phpOptions Reference
--dry-run
Preview changes without modifying files:
testlink sync --dry-runSee Dry Run Mode for details.
--path
Limit sync to a specific source directory:
testlink sync --path=src/ServicesOnly #[TestedBy] attributes in src/Services will be processed.
--link-only
Use links() / #[Links] instead of linksAndCovers() / #[LinksAndCovers]:
testlink sync --link-onlyUseful for integration/e2e tests where coverage is tracked separately.
--prune
Remove orphaned link calls:
testlink sync --prune --forceSee Pruning Orphans for details.
--force
Required when using --prune as a safety measure:
# This will fail
testlink sync --prune
# This works
testlink sync --prune --force--framework
Target a specific framework:
# Sync only Pest tests
testlink sync --framework=pest
# Sync only PHPUnit tests
testlink sync --framework=phpunit
# Auto-detect (default)
testlink sync --framework=autoGenerated Code Format
Pest Format
Input:
#[TestedBy(UserServiceTest::class, 'creates user')]Output:
->linksAndCovers(UserService::class.'::create')The format uses:
- Class constant (
::class) for IDE support - String concatenation for the method name
- Single quotes for consistency
PHPUnit Format
Input:
#[TestedBy(UserServiceTest::class, 'test_creates_user')]Output:
#[LinksAndCovers(UserService::class, 'create')]Placement
test('creates user', function () {
// ...
});test('creates user', function () {
// ...
})->linksAndCovers(UserService::class.'::create');public function test_creates_user(): void
{
// ...
}#[LinksAndCovers(UserService::class, 'create')]
public function test_creates_user(): void
{
// ...
}Multiple Methods
test('checkout flow', function () {
// ...
})->linksAndCovers(CartService::class.'::checkout')
->linksAndCovers(PaymentService::class.'::charge');#[LinksAndCovers(CartService::class, 'checkout')]
#[LinksAndCovers(PaymentService::class, 'charge')]
public function test_checkout_flow(): void
{
// ...
}Error Handling
Test File Not Found
Error: File not found: tests/Unit/MissingTest.phpThe test class referenced in #[TestedBy] doesn't have a corresponding file.
Test Case Not Found
Error: Test case not found: "missing test" in tests/Unit/UserServiceTest.phpThe test name in #[TestedBy] doesn't match any test in the file.
Parse Errors
Error: Could not parse: tests/Unit/BrokenTest.phpThe test file has syntax errors that prevent parsing.
Best Practices
- Use dry-run first - Always preview before applying changes
- Commit before sync - Have a clean git state to review changes
- Run validation after - Ensure sync was successful
- Automate in CI - Consider running sync in pull request checks
Integration with Validation
After syncing, run validation to confirm everything is aligned:
testlink sync && testlink validate