You cannot test a CSMS properly with real chargers alone. Hardware is slow to set up, you cannot make it misbehave on demand, and you certainly cannot spin up five thousand of them for a load test. A charge point simulator, software that opens an OCPP connection and behaves like a charger, is the core of any serious CSMS test strategy. This guide covers what a good simulator needs, which scenarios find the bugs that reach production, and how simulator testing relates to formal OCPP certification.
What a simulator must do
At minimum, a simulator opens a WebSocket to your CSMS with the right subprotocol (ocpp1.6 or ocpp2.0.1), sends BootNotification, keeps a heartbeat, and can run a transaction. To be useful for real testing it also needs:
- A state machine per connector, so it sends status changes in a realistic order and refuses commands that do not fit its state, as real chargers do.
- Handlers for CSMS-initiated messages: remote start/stop,
Reset,ChangeConfigurationorSetVariables,SetChargingProfile,TriggerMessage,UnlockConnector, firmware and diagnostics requests. - Realistic metering: an energy register that advances in line with power, and
MeterValuesat the configured interval, honoring charging profile limits. - Fault injection: drop the connection, delay or omit responses, send malformed frames, reboot mid-transaction, report faults.
- An offline queue: buffer transaction messages while disconnected and replay them on reconnect, like real firmware.
- Scriptability: scenarios defined as code or data, so they can run in CI.
- Security profiles: Basic authentication over TLS and client certificates, if your CSMS supports them.
A minimal scenario
A basic OCPP 1.6 happy path from the simulator's side looks like this. Each CALL should get a CALLRESULT with the same message ID:
[2, "1", "BootNotification", { "chargePointVendor": "SimCo", "chargePointModel": "SIM-DC" }]
[2, "2", "StatusNotification", { "connectorId": 1, "status": "Available", "errorCode": "NoError" }]
[2, "3", "Authorize", { "idTag": "TESTTAG01" }]
[2, "4", "StatusNotification", { "connectorId": 1, "status": "Preparing", "errorCode": "NoError" }]
[2, "5", "StartTransaction", { "connectorId": 1, "idTag": "TESTTAG01", "meterStart": 120500, "timestamp": "2026-10-26T09:00:00Z" }]
[2, "6", "StatusNotification", { "connectorId": 1, "status": "Charging", "errorCode": "NoError" }]
[2, "7", "MeterValues", { "connectorId": 1, "transactionId": 5501, "meterValue": [{ "timestamp": "2026-10-26T09:01:00Z", "sampledValue": [{ "value": "121300", "measurand": "Energy.Active.Import.Register", "unit": "Wh" }] }] }]
[2, "8", "StopTransaction", { "transactionId": 5501, "meterStop": 138900, "timestamp": "2026-10-26T09:30:00Z", "reason": "Local" }]The simulator should assert on the responses too: Accepted boot status, a sensible heartbeat interval, an idTagInfo with status Accepted, and a transactionId that it then uses in later messages. The boot and heartbeat and start/stop transaction posts explain each field.
Scenarios that catch real bugs
Happy paths rarely break in production. These do:
Connectivity and ordering
- Disconnect mid-transaction, keep charging, reconnect after minutes or hours, then replay queued
MeterValuesandStopTransactionwith original timestamps. Check the session is billed correctly and not duplicated. - Reconnect while the CSMS still thinks the old socket is open. The new connection must replace the old one cleanly.
- Send
StopTransactionfor a transaction the CSMS never saw start (becauseStartTransactionwas lost). - Retry the same CALL with the same message ID after a timeout. The CSMS must not create two transactions.
- Boot with
Pendingand verify the CSMS's provisioning flow, thenAccepted.
Authorization
- Unknown, blocked, expired and concurrent-tx tokens.
- Roaming tokens that need a real-time lookup to a partner that is slow or down.
- Offline authorization with a local list, followed by a
StartTransactionthe CSMS would have rejected.
Commands
- Remote start to a connector that is
Unavailableor already charging: the simulator should reject and the CSMS should report the failure to the user. - Remote start accepted but the driver never plugs in.
- Charging profiles with stacking, overlapping validity and
TxProfilesent before and after the transaction starts. Compare the simulator's composite schedule with what the CSMS expected. See OCPP 1.6 smart charging.
Vendor quirks
Real chargers deviate from the spec in consistent ways: decimal meter values as strings with odd precision, missing optional fields, timestamps without a timezone, status sequences that skip states, DataTransfer for things the standard covers. Every quirk you meet in the field should become a simulator profile so you never regress on it. The troubleshooting post lists common ones.
Load and soak testing
A CSMS that handles ten chargers can fall over at ten thousand, usually at the WebSocket layer or the database. Run many simulator instances to test:
- Reconnect storms: every charger reconnecting and sending
BootNotificationat once, as after a deployment or network outage. This is the realistic peak, not steady-state traffic. - Steady state at your expected fleet size with realistic heartbeat and meter value intervals.
- Soak tests over days, looking for memory growth, connection leaks and slow queries as tables grow.
- Rolling deployments under load: connections should drain and move to new instances without losing in-flight messages.
Add jitter to simulator start times and intervals; perfectly synchronized simulators produce unrealistic spikes. Our CSMS architecture post covers the server side of this.
Where certification fits: OCA and OCTT
The Open Charge Alliance runs the OCPP certification program. Certification tests are executed by independent test labs using the OCPP Compliance Testing Tool (OCTT), which validates that a charging station or CSMS implements the specification correctly for the certification profiles it claims. Vendors can buy OCTT for their own pre-testing, and it can generate a signed Vendor Declaration of Conformance from a clean automated run. See OCPP certification explained.
OCTT and your own simulators answer different questions. OCTT asks whether your implementation conforms to the spec. Your simulators ask whether your platform works with the chargers you actually deploy, at your scale, through the failures you actually see. You need both.
Putting it in CI
- Run a core scenario suite on every merge: boot, authorize, full transaction, remote start/stop, reset, configuration change, for each OCPP version you support.
- Run fault and vendor-quirk scenarios nightly.
- Run load and reconnect-storm tests before releases that touch the connection layer or data model.
- Before onboarding a new charger model, record its behaviour against a staging CSMS and turn deviations into a simulator profile.
Key takeaways
- A useful simulator has a connector state machine, CSMS command handlers, realistic metering, fault injection and an offline queue.
- The bugs that matter are in reconnection, retries, offline replay and vendor deviations, not happy paths.
- OCTT checks conformance; your own simulators check that your platform works in the field. Use both.
HashTech does hardware onboarding and conformance testing as part of the charging platforms we build.

