Testing
Getting Started
Introduction Overview InstallationUsage
Daemon Managed-client Ipc-protocol Type-serializationReference
Testing ExamplesTesting
Prerequisites
Integration tests require the OPC UA test server suite running locally:
git clone https://github.com/php-opcua/uanetstandard-test-suite.git
cd uanetstandard-test-suite
docker compose up -dThe test suite provides 8 OPC UA servers on ports 4840–4847 with different security configurations.
Running Tests
# All tests
./vendor/bin/pest
# Unit tests only (no server or daemon required)
./vendor/bin/pest tests/Unit/
# Integration tests only (requires test servers)
./vendor/bin/pest tests/Integration/ --group=integration
# A specific test file
./vendor/bin/pest tests/Unit/TypeSerializerTest.php
# With coverage report
php -d pcov.enabled=1 ./vendor/bin/pest --coverageCoverage Note
SessionManagerDaemon is excluded from coverage reports (see codecov.yml). PHP coverage tools (pcov, xdebug) only instrument the process that runs the test suite — they cannot track code executing inside a child process started via proc_open().
The daemon IS fully tested by the integration test suite: TestHelper::startDaemon() starts a real daemon process, the tests send real IPC commands and verify real responses. The coverage tool simply cannot see into the subprocess.
This is a known limitation shared by other daemon/worker-based PHP packages:
- Laravel Horizon — Redis queue workers run as child processes, not covered by PHPUnit
- Symfony Messenger — consumer workers are separate processes
- ReactPHP servers — the event loop runs in a standalone process
- RoadRunner / FrankenPHP — PHP workers are spawned by the Go runtime
Without the exclusion, reported coverage would be ~82% instead of the actual ~99%+ that the full test suite provides.
Test Structure
Unit Tests
| File | Tests | Covers |
|---|---|---|
TypeSerializerTest.php |
40+ | All type serialization/deserialization roundtrips including v4.0.0 DTOs |
SessionStoreTest.php |
8 | Session CRUD, expiration, touching |
ManagedClientConfigTest.php |
17 | Configuration setters/getters, logger, cache, extension object repository |
CommandHandlerSecurityTest.php |
18 | Method whitelist, setter rejection, credential stripping, max sessions, cert validation |
BrowseDirectionSerializationTest.php |
8 | BrowseDirection, ConnectionState, BrowseNode tree serialization |
AutoGeneratedCertTest.php |
7 | Auto-generated certificate flow, cert path validation bypass |
Integration Tests
| File | Tests | Covers |
|---|---|---|
DaemonTest.php |
— | Ping, list, open/close, error handling |
ConnectionTest.php |
— | Anonymous, username/password, certificate, invalid host |
ConnectionStateTest.php |
— | isConnected, getConnectionState, reconnect |
BrowseTest.php |
— | Browse with direction, reference types, continuation |
BrowseRecursiveTest.php |
— | browseAll, browseRecursive, depth limits |
ReadTest.php |
— | Single/multi read, all scalar types |
WriteTest.php |
— | Single/multi write, all types, batching |
MethodCallTest.php |
— | Method invocation with arguments |
SubscriptionTest.php |
— | Create, monitor, publish, delete |
TranslateBrowsePathTest.php |
— | Path resolution, namespace paths |
TimeoutAndBatchingTest.php |
— | Timeout, batching, server limits |
SessionPersistenceTest.php |
— | Cross-instance, state isolation |
SecurityTest.php |
— | Auth token, method whitelist, credential stripping |
AutoCertConnectionTest.php |
— | Auto-generated certificates |
TestHelper
The TestHelper class manages daemon lifecycle and provides client utilities:
use PhpOpcua\SessionManager\Tests\Integration\Helpers\TestHelper;
// Daemon lifecycle
TestHelper::startDaemon(); // starts daemon on test socket
TestHelper::stopDaemon(); // graceful shutdown
TestHelper::isDaemonRunning(); // check status
// Client helpers
$client = TestHelper::createManagedClient();
$client = TestHelper::connectNoSecurity();
$nodeId = TestHelper::browseToNode($client, ['TestServer', 'DataTypes', 'Scalar']);
$ref = TestHelper::findRefByName($refs, 'BooleanValue');
TestHelper::safeDisconnect($client);Usage in Tests
beforeAll(fn() => TestHelper::startDaemon());
afterAll(fn() => TestHelper::stopDaemon());
it('reads a value', function () {
$client = null;
try {
$client = TestHelper::connectNoSecurity();
$value = $client->read(NodeId::numeric(0, 2259));
expect($value->statusCode)->toBe(0);
} finally {
TestHelper::safeDisconnect($client);
}
})->group('integration');Test Endpoints
| Constant | Port | Description |
|---|---|---|
ENDPOINT_NO_SECURITY |
4840 | Anonymous, no encryption |
ENDPOINT_USERPASS |
4841 | Username/password authentication |
ENDPOINT_CERTIFICATE |
4842 | Certificate authentication |
ENDPOINT_ALL_SECURITY |
4843 | All security options |
ENDPOINT_DISCOVERY |
4844 | Endpoint discovery |
ENDPOINT_AUTO_ACCEPT |
4845 | Auto-accept certificates |
ENDPOINT_SIGN_ONLY |
4846 | Sign-only mode |
ENDPOINT_LEGACY |
4847 | Legacy security policies |