Testing with pymodbus Simulator #2087
-
(Dear developers, thank you very much for your work. I enjoy using the package!) Recently, on a new project, we started using
What we do nowWhen we start developing, we run a simple bash command: ./venv/bin/pymodbus.simulator --json_file ./simulator_setup.json --log debug which starts a configured simulator that mocks the real Modbus Server the code is supposed to interact with. This works great and achieves our objective – but when running tests, this approach is not ideal. Before automatically testing our code (using pytest), we manually start the Simulator and run the tests. The tests could look like this: class ExampleDeviceModbusClient:
def __init__(self, host: str, port: int):
self.client = ModbusTcpClient(host, port)
def check_connection(self) -> bool:
"""
Check if the Modbus device is reachable.
Returns True if reachable, False otherwise.
"""
try:
# Simple diagnostic request
result = self.client.read_device_information()
return not result.isError()
except ConnectionException:
return False
@pytest.fixture
def device():
return ExampleDeviceModbusClient("0.0.0.0", port=503)
def test_connection(device: ExampleDeviceModbusClient):
assert device.check_connection() in [True, False]
What we'd like to doThe workflow above works, but is not ideal. The optimal solution would be to start Simulator only before test and close it after tests are done. That would allow us to automate the testing in some CI system, like Github Actions. I have tried some ways, but failed. My guess is that I don't understand how Simulator is started directly from python. My attempt (not working): import multiprocessing
import pytest
from pymodbus.client import ModbusTcpClient
from pymodbus.exceptions import ConnectionException
from pymodbus.server import ModbusSimulatorServer
async def run_simulator():
task = ModbusSimulatorServer(json_file="../simulator_setup.json")
await task.run_forever(only_start=True)
@pytest.fixture(autouse=True)
def run_around_tests():
# Code that will run before your test, for example:
p = multiprocessing.Process(target=run_simulator)
# A test function will be run at this point
yield
# Code that will run after your test, for example:
p.close()
class ExampleDeviceModbusClient:
def __init__(self, host: str, port: int):
self.client = ModbusTcpClient(host, port)
def check_connection(self) -> bool:
"""
Check if the Modbus device is reachable.
Returns True if reachable, False otherwise.
"""
try:
# Simple diagnostic request
result = self.client.read_device_information()
return not result.isError()
except ConnectionException:
return False
@pytest.fixture
def device():
return ExampleDeviceModbusClient("0.0.0.0", port=503)
def test_connection(device: ExampleDeviceModbusClient):
assert device.check_connection() in [True, False] Do you have any experience or success with automatic testing using pymodbus.simulator? How could I make this work? Would you reccommend any other approach? |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment
-
There are several easy ways to do that. The best way is to use pytest functionality like e.g.: We do it slightly different with a fixture, that yield to the test...but this is because we want the server to start/stop with each single test. see test/conftest system_health_check |
Beta Was this translation helpful? Give feedback.
There are several easy ways to do that.
The best way is to use pytest functionality like e.g.:
https://stackoverflow.com/questions/7786648/how-to-call-setup-once-for-all-tests-and-teardown-after-all-are-finished
We do it slightly different with a fixture, that yield to the test...but this is because we want the server to start/stop with each single test.
see test/conftest system_health_check