# plesty init mock-test Adds the hardware-free test gate d1 requires to an **existing** device module. Unlike [`plesty init field-test`](init-field-test.md), it asks nothing. ```bash cd plesty-pm100d plesty init mock-test # writes tests/test_mock_pipeline.py plesty init mock-test --force # regenerate over its own previous output ``` The field-test interview exists because every one of its questions is a safety judgement about a real instrument. Nothing in the mock pipeline can move a stage or fire an exposure, and everything it needs is either declared by the device or settled by trying it: | Source | What it gives | |---|---| | the device's `doc_model()` | the parameters and operations, read through the same reader the field test uses | | the package name | the module and the import | | a construction probe | how to reach the device without hardware | ## The probe **The probe is the design.** A device module is hardware-free in one of several ways — `address="mock"` selects an in-memory simulator in one reference module, `use_mock_solver=True` does it in the other — and which one applies is knowable by constructing the device and looking. Each candidate is verified *the way the gates will verify it*: connect, read `is_operatable`, call `identity()`, and call `check_errors()`, which must return a list — empty on a fresh device. A strategy is written into the file only once it has passed the checks the file will make, which is why a generated test that gets written is a generated test that passes those checks. When neither applies, the last resort is the module's own `DocDevice` — the hardware-free construction gate d3 already requires. A module scaffolded by `plesty init device` exports it as `partial(Device, id=...)`, and because `DevicePipeline` constructs from a class, the file passes `Device` and forwards the partial's own arguments: ```python PIPELINE = DevicePipeline(Device, *DocDevice.args, **DocDevice.keywords) ``` A `DocDevice` that is neither a class nor a partial of `Device` is refused with that reason. Where no strategy works the command refuses and lists what it tried. It does not fall back to asking: a developer handed a question the probe could not answer would be guessing at the same thing, and a file built on a guess looks complete and tests less than it claims. Give the device a hardware-free path — `address="mock"` is the convention, and `plesty.lib.sim.demo_device.DemoDevice` is the reference implementation of it — then run the command again. The probe covers three of the eight gates. On a fresh scaffold the file is written and gate 2 (parameter key resolution) still fails until the device declares its schema — the first of the next steps `plesty init device` prints. ## The generated file The generated file is short, because `run_mock_pipeline()` runs all eight gates and raises with everything that failed: ```python from plesty.lib.test.device_pipeline import DevicePipeline from plesty.pm100d import Device PIPELINE = DevicePipeline(Device, address="mock") def test_mock_pipeline() -> None: """Run the eight mock gates and fail with everything that broke.""" PIPELINE.run_mock_pipeline() ``` No schema path appears in it. The gates read what the device declares at run time, so nothing here restates a schema and nothing goes stale when the device gains a parameter. The file is always `tests/test_mock_pipeline.py`, never `tests/test_.py` — that is where a module's hand-written unit tests already live, and one reference module keeps 487 lines of them. `--force` regenerates over this command's own output and refuses anything else, checked by a marker in the file, so a regeneration cannot destroy work.