Testing
Getting Started
Introduction InstallationUsage
Usage Connections Session-manager Logging-caching SecurityReference
Testing Examples Auto-publishTesting
MockClient
The underlying opcua-client provides a MockClient — a drop-in test double that implements OpcUaClientInterface with no TCP connection. Use it to test your application logic without a real OPC UA server.
Basic Usage
use PhpOpcua\Client\Testing\MockClient;
use PhpOpcua\Client\Types\DataValue;
$mock = MockClient::create()
->onRead('i=2259', fn() => DataValue::ofInt32(0))
->onRead('ns=2;s=Temperature', fn() => DataValue::ofDouble(23.5));
$value = $mock->read('i=2259');
echo $value->getValue(); // 0Register Handlers
$mock = MockClient::create()
->onRead($nodeId, fn() => DataValue::ofInt32(42))
->onWrite($nodeId, fn() => StatusCode::Good)
->onBrowse($nodeId, fn() => [...$references])
->onCall($objectId, $methodId, fn(array $args) => new CallResult(...))
->onResolveNodeId($path, fn() => new NodeId(0, 2253))
->onGetEndpoints(fn() => [...$endpoints]); // v4.0+v4.0+ MockClient Methods
The MockClient in v4 supports additional methods for testing trust store, event, and subscription features:
$mock = MockClient::create()
// Event dispatcher
->setEventDispatcher($dispatcher)
// Endpoint discovery handler
->onGetEndpoints(fn() => [...$endpoints])
// Write type auto-detection (type parameter is now optional)
->setAutoDetectWriteType(true)
// Read metadata cache
->setReadMetadataCache(true)
// Trust store
->setTrustStorePath('/tmp/test-trust')
->setTrustPolicy(TrustPolicy::Fingerprint);Additional operations available on the mock:
// Modify monitored items
$mock->modifyMonitoredItems($subId, [
['monitoredItemId' => 1, 'samplingInterval' => 200.0],
]);
// Set triggering links
$mock->setTriggering($subId, $triggeringItemId, [2, 3], []);Note: In v4, the
write()type parameter is nullable.MockClientreflects this -- you can call$mock->write('ns=2;i=1001', 42)without specifying a type, and it will use theonWritehandler as usual.
Call Tracking
$mock->read('i=2259');
$mock->read('i=2256');
echo $mock->callCount('read'); // 2
echo count($mock->getCalls()); // 2
echo count($mock->getCallsFor('read')); // 2
$mock->resetCalls();
echo $mock->callCount('read'); // 0DataValue Factory Methods
DataValue::ofBoolean(true);
DataValue::ofInt32(42);
DataValue::ofUInt32(100);
DataValue::ofDouble(3.14);
DataValue::ofString('hello');
DataValue::ofFloat(2.5);
DataValue::of($value, BuiltinType::Int32);
DataValue::bad(StatusCode::BadNodeIdUnknown);Injecting MockClient into OpcuaManager
use PhpOpcua\SymfonyOpcua\OpcuaManager;
$mock = MockClient::create()
->onRead('i=2259', fn() => DataValue::ofInt32(0));
$manager = new OpcuaManager([
'default' => 'default',
'session_manager' => ['enabled' => false, 'socket_path' => '/tmp/nonexistent.sock'],
'connections' => ['default' => ['endpoint' => 'opc.tcp://localhost:4840']],
]);
// Replace the connection with the mock
$ref = new \ReflectionProperty($manager, 'connections');
$ref->setValue($manager, ['default' => $mock]);
$value = $manager->connection('default')->read('i=2259');
echo $value->getValue(); // 0
echo $mock->callCount('read'); // 1Running Tests
Unit Tests
No external dependencies required:
./vendor/bin/pest tests/Unit/Integration Tests
Require the uanetstandard-test-suite Docker containers:
git clone https://github.com/php-opcua/uanetstandard-test-suite.git
cd uanetstandard-test-suite
docker compose up -dThen run:
./vendor/bin/pest tests/Integration/ --group=integrationAll Tests
./vendor/bin/pestCoverage
php -d pcov.enabled=1 ./vendor/bin/pest --coverageTarget: 99.5%+ code coverage.
Test Structure
tests/
├── Unit/
│ ├── ConfigTest.php # YAML config validation
│ ├── MockClientTest.php # MockClient integration
│ ├── OpcuaManagerTest.php # Manager logic, configureBuilder
│ └── PhpOpcuaSymfonyOpcuaBundleTest.php # Bundle registration, DI wiring
└── Integration/
├── Helpers/
│ └── TestHelper.php # Endpoints, config builders, daemon management
├── ConnectionTest.php # Direct and managed connections
├── ConnectionStateTest.php # isConnected, reconnect
├── ReadTest.php # Scalar, array, multi-read
├── WriteTest.php # Scalar, array, multi-write
├── BrowseTest.php # Browse, continuation, NodeClass filter
├── BrowseRecursiveTest.php # browseAll, browseRecursive, maxDepth
├── TranslateBrowsePathTest.php # resolveNodeId, translateBrowsePaths
├── MethodCallTest.php # Method invocation
├── SubscriptionTest.php # Create, monitor, publish, delete
├── SubscriptionTransferTest.php # transferSubscriptions, republish
├── HistoryReadAdvancedTest.php # historyReadRaw, historyReadAtTime
├── TimeoutTest.php # Timeout config and fluent API
├── AutoRetryTest.php # Auto-retry config and fluent API
├── BatchingTest.php # Server limits, transparent batching
├── StringNodeIdTest.php # String NodeId in read, browse, readMulti
├── BuilderApiTest.php # Fluent builders for read, write, paths
├── CacheTest.php # setCache, invalidateCache, flushCache, useCache
├── LoggerTest.php # setLogger, getLogger
├── TypeDiscoveryTest.php # discoverDataTypes, getExtensionObjectRepository
├── DynamicVariableTest.php # Counter, Random, SineWave, Timestamp
└── ErrorHandlingTest.php # BadNodeIdUnknown, read-only writesTestHelper
The TestHelper class provides:
- Endpoint constants — 8 test server endpoints (no-security, userpass, certificate, auto-accept, etc.)
- Daemon management —
startDaemon()/stopDaemon()for integration tests - Config builders —
makeDirectConfig(),makeManagedConfig(),createDirectManager(),createManagedManager() - Browse helpers —
browseToNode(),findRefByName(),safeDisconnect()
All integration tests run in both direct and managed (daemon) modes using a foreach loop pattern.