PHPackages                             testflowlabs/testlink - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. [Testing &amp; Quality](/categories/testing)
4. /
5. testflowlabs/testlink

ActiveLibrary[Testing &amp; Quality](/categories/testing)

testflowlabs/testlink
=====================

Framework-agnostic test traceability for PHP. Supports Pest and PHPUnit with bidirectional code-to-test linking.

v1.1.0(5mo ago)53MITPHPPHP ^8.3CI passing

Since Dec 13Pushed 5mo agoCompare

[ Source](https://github.com/TestFlowLabs/testlink)[ Packagist](https://packagist.org/packages/testflowlabs/testlink)[ Docs](https://link.testflowlabs.dev/)[ GitHub Sponsors](https://github.com/deligoez)[ RSS](/packages/testflowlabs-testlink/feed)WikiDiscussions master Synced 1mo ago

READMEChangelog (3)Dependencies (8)Versions (5)Used By (0)

 [![TestLink Logo](docs/public/testlink-logo.png)](docs/public/testlink-logo.png)

TestLink
========

[](#testlink)

 **Framework-Agnostic Test Traceability for PHP**

 [![PHP Version](https://camo.githubusercontent.com/ef0054230522e542bc1f908ac005c6c75888dea255bac910f9015e12095e31d7/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f7068702d253545382e332d626c7565)](https://php.net) [![Pest Support](https://camo.githubusercontent.com/a2b5bd7b786525b93f936d1d18f1360291132ca89f1d4f17c4654d11670b06ac/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706573742d253545342e302d6f72616e6765)](https://pestphp.com) [![PHPUnit Support](https://camo.githubusercontent.com/9f10f86fe4e4adcb4925bc7173112cba60390e75593cc78da201094a4018cda2/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f706870756e69742d25354531312e302d707572706c65)](https://phpunit.de) [![License](https://camo.githubusercontent.com/f8df3091bbe1149f398a5369b2c39e896766f9f6efba3477c63e9b4aa940ef14/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d677265656e)](LICENSE)

TestLink creates bidirectional links between your tests and production code. Know exactly which tests cover each method, and which methods each test exercises.

**Supports both Pest and PHPUnit** - use whichever testing framework you prefer, or both in the same project.

Features
--------

[](#features)

- **Framework Agnostic** - Works with Pest, PHPUnit, or both
- **Bidirectional Linking** - Link tests to methods AND methods to tests
- **Two Link Types** - Coverage links (`linksAndCovers`) and traceability-only links (`links`)
- **Sync Validation** - Detect missing or orphaned links
- **Auto-Sync** - Generate link calls from `#[TestedBy]` attributes
- **Standalone CLI** - Use `testlink` command independently of test runner
- **JSON Export** - CI/CD integration

Quick Start
-----------

[](#quick-start)

### Installation

[](#installation)

```
# Production dependency - attributes for production code
composer require testflowlabs/test-attributes

# Dev dependency - CLI tools, scanners, validators
composer require --dev testflowlabs/testlink
```

**Why two packages?**

- `test-attributes` must be a **production** dependency because `#[TestedBy]`, `#[LinksAndCovers]`, and `#[Links]` attributes are placed on production classes. PHP needs these attribute classes available when autoloading your production code.
- `testlink` can be a **dev** dependency because it only provides CLI tools (`testlink report`, `testlink validate`, `testlink sync`) that run during development.

### Link from Production Code (Recommended)

[](#link-from-production-code-recommended)

```
// app/Services/UserService.php
use TestFlowLabs\TestingAttributes\TestedBy;

class UserService
{
    #[TestedBy(UserServiceTest::class, 'it creates a user')]
    #[TestedBy(UserServiceTest::class, 'it validates email format')]
    public function create(array $data): User
    {
        // ...
    }
}
```

### Link from Tests

[](#link-from-tests)

#### Pest

[](#pest)

```
// tests/Unit/UserServiceTest.php

// Link + Coverage (triggers coverage tracking)
test('it creates a user')
    ->linksAndCovers(UserService::class.'::create');

// Link only (traceability without coverage)
test('it creates a user integration')
    ->links(UserService::class.'::create');

// Multiple methods
test('it validates and creates')
    ->linksAndCovers(UserService::class.'::validate')
    ->linksAndCovers(UserService::class.'::create');
```

#### PHPUnit

[](#phpunit)

```
// tests/Unit/UserServiceTest.php
use TestFlowLabs\TestingAttributes\LinksAndCovers;
use TestFlowLabs\TestingAttributes\Links;

class UserServiceTest extends TestCase
{
    // Link + Coverage
    #[LinksAndCovers(UserService::class, 'create')]
    public function testItCreatesUser(): void
    {
        // ...
    }

    // Link only
    #[Links(UserService::class, 'create')]
    public function testItCreatesUserIntegration(): void
    {
        // ...
    }

    // Multiple methods
    #[LinksAndCovers(UserService::class, 'validate')]
    #[LinksAndCovers(UserService::class, 'create')]
    public function testItValidatesAndCreates(): void
    {
        // ...
    }
}
```

CLI Commands
------------

[](#cli-commands)

```
# Show coverage links report
testlink report

# Validate bidirectional sync
testlink validate

# Auto-sync from #[TestedBy] to test files
testlink sync

# Preview sync changes (dry run)
testlink sync --dry-run

# Sync and prune orphaned links
testlink sync --prune --force

# Export as JSON
testlink report --json

# Show help
testlink --help
testlink sync --help
```

### Sample Output

[](#sample-output)

```
  Coverage Links Report
  ─────────────────────

  App\Services\UserService

    create()
      → Tests\Unit\UserServiceTest::it creates a user
      → Tests\Unit\UserServiceTest::it validates email format

    update()
      → Tests\Unit\UserServiceTest::it updates a user

  Summary
    Methods with tests: 2
    Total test links: 3

```

Validation
----------

[](#validation)

The `validate` command checks for:

- **Missing Coverage**: Production methods with `#[TestedBy]` but no matching link call
- **Orphaned Links**: Tests claiming links that don't exist
- **Sync Issues**: Mismatched bidirectional links

```
$ testlink validate

  Validation Report
  ─────────────────

  Missing Link Calls in Tests
  These #[TestedBy] attributes have no corresponding link calls:

    ✗ App\Services\OrderService::process
      → Tests\Unit\OrderServiceTest::it processes order

  Orphaned Link Calls in Tests
  These link calls have no corresponding #[TestedBy] attributes:

    ! Tests\Unit\PaymentTest::it charges card
      → App\Services\PaymentService::charge

  Validation failed. Run "testlink sync" to fix issues.
```

Auto-Sync
---------

[](#auto-sync)

Automatically generate link calls from `#[TestedBy]` attributes:

```
# Preview what will change
testlink sync --dry-run

# Apply sync
testlink sync

# Sync and remove orphaned links
testlink sync --prune --force
```

### How It Works

[](#how-it-works)

1. Scans production code for `#[TestedBy]` attributes
2. Locates corresponding test files and test cases
3. Adds missing `linksAndCovers()` calls (Pest) or `#[LinksAndCovers]` attributes (PHPUnit)

### Before Sync (Pest)

[](#before-sync-pest)

```
// Production code has the attribute
#[TestedBy(UserServiceTest::class, 'it creates a user')]
public function create(): User { }

// Test file is missing link
test('it creates a user', function () { });
```

### After Sync (Pest)

[](#after-sync-pest)

```
test('it creates a user', function () {
    // ...
})->linksAndCovers(UserService::class.'::create');
```

### Before Sync (PHPUnit)

[](#before-sync-phpunit)

```
// Production code has the attribute
#[TestedBy(UserServiceTest::class, 'testItCreatesUser')]
public function create(): User { }

// Test file is missing attribute
public function testItCreatesUser(): void { }
```

### After Sync (PHPUnit)

[](#after-sync-phpunit)

```
#[LinksAndCovers(UserService::class, 'create')]
public function testItCreatesUser(): void { }
```

Link Types
----------

[](#link-types)

TypePest MethodPHPUnit AttributePurpose**Link + Coverage**`->linksAndCovers()``#[LinksAndCovers]`Traceability + triggers coverage tracking**Link Only**`->links()``#[Links]`Traceability only, no coverageUse **Link + Coverage** for unit tests where you want coverage tracking. Use **Link Only** for integration/e2e tests where unit coverage is already tracked elsewhere.

JSON Export
-----------

[](#json-export)

For CI/CD integration:

```
testlink report --json > coverage-links.json
```

```
{
  "links": {
    "App\\Services\\UserService::create": [
      "Tests\\Unit\\UserServiceTest::it creates a user"
    ]
  },
  "summary": {
    "total_methods": 1,
    "total_tests": 1
  }
}
```

Bootstrap (Pest)
----------------

[](#bootstrap-pest)

Add to `tests/Pest.php` to enable `linksAndCovers()` and `links()` methods:

```
use TestFlowLabs\TestLink\Runtime\RuntimeBootstrap;

RuntimeBootstrap::init();
```

Best Practices
--------------

[](#best-practices)

### 1. Prefer `#[TestedBy]` Attributes

[](#1-prefer-testedby-attributes)

Placing links in production code keeps coverage visible where it matters:

```
#[TestedBy(UserServiceTest::class, 'it creates a user')]
public function create(): User
{
    // Reader immediately knows this method is tested
}
```

### 2. Use Link Types Appropriately

[](#2-use-link-types-appropriately)

```
// Unit test - use linksAndCovers for coverage
test('it creates a user with valid data')
    ->linksAndCovers(UserService::class.'::create');

// Integration test - use links for traceability only
test('it creates user through API endpoint')
    ->links(UserService::class.'::create');
```

### 3. Run Validation in CI

[](#3-run-validation-in-ci)

```
# .github/workflows/test.yml
- name: Validate coverage links
  run: testlink validate --strict
```

Hybrid Projects
---------------

[](#hybrid-projects)

TestLink seamlessly supports projects using both Pest and PHPUnit:

```
$ testlink report

  Coverage Links Report
  ─────────────────────

  Detected frameworks: pest, phpunit

  App\Services\UserService
    create()
      → Tests\Unit\UserServiceTest::it creates a user (pest)
      → Tests\Integration\UserApiTest::testCreateUser (phpunit)
```

Documentation
-------------

[](#documentation)

Full documentation is available at the [TestLink Documentation](https://testflowlabs.github.io/testlink/).

- **[Tutorials](https://testflowlabs.github.io/testlink/tutorials/)** - Learn TestLink step-by-step with TDD and BDD workflows
- **[How-to Guides](https://testflowlabs.github.io/testlink/how-to/)** - Solve specific problems and tasks
- **[Reference](https://testflowlabs.github.io/testlink/reference/)** - CLI commands, attributes, and configuration
- **[Explanation](https://testflowlabs.github.io/testlink/explanation/)** - Understand bidirectional linking concepts

Ecosystem
---------

[](#ecosystem)

TestLink is part of the TestFlowLabs ecosystem:

PackageDescription[test-attributes](https://github.com/TestFlowLabs/test-attributes)PHP attributes for test metadata (`#[LinksAndCovers]`, `#[Links]`)[testlink](https://github.com/TestFlowLabs/testlink)This package - Test traceability, `#[TestedBy]` attribute, CLI toolsContributing
------------

[](#contributing)

1. Fork the repository
2. Create a feature branch
3. Run `composer test` to ensure all checks pass
4. Submit a pull request

License
-------

[](#license)

MIT License. See [LICENSE](LICENSE) for details.

###  Health Score

38

—

LowBetter than 85% of packages

Maintenance72

Regular maintenance activity

Popularity8

Limited adoption so far

Community8

Small or concentrated contributor base

Maturity53

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 98.9% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~0 days

Total

3

Last Release

155d ago

### Community

Maintainers

![](https://avatars.githubusercontent.com/u/3030815?v=4)[Yunus Emre Deligöz](/maintainers/deligoez)[@deligoez](https://github.com/deligoez)

---

Top Contributors

[![deligoez](https://avatars.githubusercontent.com/u/3030815?v=4)](https://github.com/deligoez "deligoez (177 commits)")[![claude](https://avatars.githubusercontent.com/u/81847?v=4)](https://github.com/claude "claude (2 commits)")

---

Tags

phptestingphpunitpestcoveragetraceabilitytestlink

###  Code Quality

TestsPest

Code StyleLaravel Pint

### Embed Badge

![Health badge](/badges/testflowlabs-testlink/health.svg)

```
[![Health](https://phpackages.com/badges/testflowlabs-testlink/health.svg)](https://phpackages.com/packages/testflowlabs-testlink)
```

###  Alternatives

[robiningelbrecht/phpunit-coverage-tools

PHPUnit coverage tools

1783.0k34](/packages/robiningelbrecht-phpunit-coverage-tools)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
