t3ide / php-lsp-test
PHPUnit-first LSP blackbox testing tools for PHP
Requires
- php: ^8.3
- psr/log: ^3.0
- symfony/process: ^7.2
- t3ide/language-server: ^1.0 || dev-main
- t3ide/language-server-protocol: ^1.0 || dev-main
Requires (Dev)
- ergebnis/composer-normalize: ^2.47
- friendsofphp/php-cs-fixer: ^3.86
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^10.5
This package is not auto-updated.
Last update: 2026-04-20 17:07:20 UTC
README
php-lsp-test is a lightweight test harness for Language Server Protocol (LSP) servers in PHP projects, built for reliable use with PHPUnit.
It is inspired by pytest-lsp, but intentionally adapted to PHP and synchronous PHPUnit workflows instead of async pytest fixtures.
Goals
- Start and control real LSP server processes from tests
- Send and receive JSON-RPC messages over stdio
- Assert protocol behavior in unit, integration, and end-to-end scenarios
- Reproduce editor-like flows in a deterministic PHPUnit environment
- Surface actionable diagnostics when tests fail (stderr/stdout/log messages)
Installation
Add as a dev dependency:
composer require --dev t3ide/php-lsp-test
Quickstart (PHPUnit)
Use LanguageServerTestCase to spin up and initialize a client quickly:
<?php declare(strict_types=1); use T3IDE\LanguageServerProtocol\Structure\InitializeParams; use T3IDE\PhpLspTest\Capability\CapabilityRepository; use T3IDE\PhpLspTest\PHPUnit\LanguageServerTestCase; final class MyLspTest extends LanguageServerTestCase { public function testServerInitialize(): void { $capabilities = CapabilityRepository::withDefaultSnapshots()->get('generic'); $client = $this->initializeLanguageClient( ['php', '/path/to/server.php'], new InitializeParams(1, 'file:///workspace', $capabilities), ); // ... send requests / assert responses ... $client->shutdownSession(); } }
Capabilities
Capability snapshots are managed by CapabilityRepository.
Built-in usage includes:
generic- version selectors like:
name@latestname@v1name@1.2.0
This enables reproducible client simulation while still supporting latest-style test targets.
Diagnostics on Failures
LanguageServerTestCase appends client diagnostics to test failures:
- process running state, pid, and exit code
- collected stderr/stdout
- recent
window/logMessageentries
For protocol-level failures, termination details are also surfaced (exit code plus stderr), which helps with server crash and broken-pipe debugging.
Protocol and Process Robustness
php-lsp-test includes coverage for edge cases such as:
- server exits while waiting for notifications
- invalid JSON payloads from server
- server termination mid request sequence
- explicit
result: nullJSON-RPC response handling
Extensibility
LanguageClient supports overriding default handlers for server requests and notifications, enabling test-specific behavior without forking core client logic.
Relationship to pytest-lsp
This project borrows core ideas from pytest-lsp:
- realistic client/server test orchestration
- capability-driven checks
- strong diagnostics around process and protocol failures
Implementation choices are intentionally PHP-first:
- synchronous API design suitable for PHPUnit
- typed PHP structures and exception model
- process lifecycle and teardown behavior aligned with common PHP CI setups
License
MIT. See LICENSE.md.