pytest-reserial


Namepytest-reserial JSON
Version 0.3.0 PyPI version JSON
download
home_pageNone
SummaryPytest fixture for recording and replaying serial port traffic.
upload_time2024-02-08 09:05:48
maintainerNone
docs_urlNone
authorNone
requires_python>=3.8
licenseNone
keywords serial testing logging mocking patching stubbing record replay
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # pytest-reserial

![build](https://github.com/bessman/pytest-reserial/actions/workflows/main.yml/badge.svg)
[![PyPI](https://img.shields.io/pypi/v/pytest-reserial.svg)](https://pypi.org/project/pytest-reserial/)
[![License](https://img.shields.io/pypi/l/pytest-reserial)](https://mit-license.org/)

Pytest plugin for recording and replaying serial port traffic during tests.

## Installation

`pip install pytest-reserial`

## Usage

1.  Write your tests as if they would run with the device connected.

2.  When your tests pass with the device connected, run `pytest --record` to record serial traffic 
    from the passing tests.

3.  Now you can disconnect the device and run your tests with `pytest --replay`.

A simple example:

```python
# my_serial_app.py
from serial import Serial

def my_serial_app():
    with Serial(port=/dev/ttyUSB0) as s:
        # When we send '1' to the device, it responds with '2'.
        s.write(b"\x01")
        return s.read()

# Just use the 'reserial' fixture!
def test_my_serial_app(reserial):
    assert my_serial_app() == b"\x02"
```

Next:

1.  Connect the device.
2.  Run `pytest my_serial_app.py` and verify that the test passes with the device connected.
3.  Run `pytest --record my_serial_app.py`. The test will run again, and the traffic will be recorded.
4.  Disconnect the device.
5.  Run `pytest --replay my_serial_app.py`. The test will pass!

The logged traffic will be stored in JSON files in the same directory as your test files, and will have the same names as the test files except with a .json extension instead of .py. For example, if your project layout is:

```shell
├── src
│   ├── myproject
│   │   ├── ...
├── tests
│   ├── test_myproject.py
```

Then after running `pytest --record`, the test/ directory will contain a new file, test_myproject.json, containing the recorded serial traffic from the tests.

## Why

Have you ever tried to write tests for a program that talks to an external device over serial (like an Arduino or something)? You probably wrote the tests assuming that the device is question would always be connected when running the tests, right? And later you got bit by one or more of the pitfalls of that approach:

-   You wanted to run the tests when the device wasn't connected. Perhaps you were travelling, or
    someone had borrowed it. Whatever the reason, you found yourself unable to run the tests, and
    therefore unable to continue development, until you could connect the device again.

-   You made a change to your program, and one of your tests failed. So far so good, right? That's
    what tests are for, after all. Only, you can't figure out why the test is failing. You spend
    several hours trying to fix it, but eventually give up and revert your changes.
    But the test still fails.
    So you try another device, and sure enough, now it passes. Turns out, what you thought was a
    problem with your code was actually a hardware failure.

-   Some of the tests depend on the device being in a certain state, and some of the tests depend on
    the device being in *another* state. So you can't run the entire test suite all at once, instead
    being forced to stop it halfway through and mess with a bunch of wires and buttons before you can
    run the rest of the tests.
   
And then you asked yourself, 'How do I write my tests so that the device doesn't need to be connected?' You may have gone down the rabbit hole that is mocking, and then replaced large parts of pyserial with mock interfaces, and ultimately ended up with a test suite that was significantly more complex than the program it was meant to test.

With pytest-reserial, you don't have to worry about any of that. Just write your tests as if the device is always connected. Then, simply use the `reserial` fixture to record the serial traffic from passing tests, and replay it when the device isn't connected.

## Requirements

pytest-reserial depends on pytest and pyserial.

If jsbeautifier is installed, each test's recorded traffic is written on a single line in the log files. Otherwise, each recorded byte is written on a separate line.

## Copyright

MIT License, (C) 2022 Alexander Bessman

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "pytest-reserial",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": null,
    "keywords": "serial,testing,logging,mocking,patching,stubbing,record,replay",
    "author": null,
    "author_email": "Alexander Bessman <alexander.bessman@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/8c/04/c6c9d380db0823ca3db548bee469ee3ff1f737998273039c464955c3b7f3/pytest_reserial-0.3.0.tar.gz",
    "platform": null,
    "description": "# pytest-reserial\n\n![build](https://github.com/bessman/pytest-reserial/actions/workflows/main.yml/badge.svg)\n[![PyPI](https://img.shields.io/pypi/v/pytest-reserial.svg)](https://pypi.org/project/pytest-reserial/)\n[![License](https://img.shields.io/pypi/l/pytest-reserial)](https://mit-license.org/)\n\nPytest plugin for recording and replaying serial port traffic during tests.\n\n## Installation\n\n`pip install pytest-reserial`\n\n## Usage\n\n1.  Write your tests as if they would run with the device connected.\n\n2.  When your tests pass with the device connected, run `pytest --record` to record serial traffic \n    from the passing tests.\n\n3.  Now you can disconnect the device and run your tests with `pytest --replay`.\n\nA simple example:\n\n```python\n# my_serial_app.py\nfrom serial import Serial\n\ndef my_serial_app():\n    with Serial(port=/dev/ttyUSB0) as s:\n        # When we send '1' to the device, it responds with '2'.\n        s.write(b\"\\x01\")\n        return s.read()\n\n# Just use the 'reserial' fixture!\ndef test_my_serial_app(reserial):\n    assert my_serial_app() == b\"\\x02\"\n```\n\nNext:\n\n1.  Connect the device.\n2.  Run `pytest my_serial_app.py` and verify that the test passes with the device connected.\n3.  Run `pytest --record my_serial_app.py`. The test will run again, and the traffic will be recorded.\n4.  Disconnect the device.\n5.  Run `pytest --replay my_serial_app.py`. The test will pass!\n\nThe logged traffic will be stored in JSON files in the same directory as your test files, and will have the same names as the test files except with a .json extension instead of .py. For example, if your project layout is:\n\n```shell\n\u251c\u2500\u2500 src\n\u2502   \u251c\u2500\u2500 myproject\n\u2502   \u2502   \u251c\u2500\u2500 ...\n\u251c\u2500\u2500 tests\n\u2502   \u251c\u2500\u2500 test_myproject.py\n```\n\nThen after running `pytest --record`, the test/ directory will contain a new file, test_myproject.json, containing the recorded serial traffic from the tests.\n\n## Why\n\nHave you ever tried to write tests for a program that talks to an external device over serial (like an Arduino or something)? You probably wrote the tests assuming that the device is question would always be connected when running the tests, right? And later you got bit by one or more of the pitfalls of that approach:\n\n-   You wanted to run the tests when the device wasn't connected. Perhaps you were travelling, or\n    someone had borrowed it. Whatever the reason, you found yourself unable to run the tests, and\n    therefore unable to continue development, until you could connect the device again.\n\n-   You made a change to your program, and one of your tests failed. So far so good, right? That's\n    what tests are for, after all. Only, you can't figure out why the test is failing. You spend\n    several hours trying to fix it, but eventually give up and revert your changes.\n    But the test still fails.\n    So you try another device, and sure enough, now it passes. Turns out, what you thought was a\n    problem with your code was actually a hardware failure.\n\n-   Some of the tests depend on the device being in a certain state, and some of the tests depend on\n    the device being in *another* state. So you can't run the entire test suite all at once, instead\n    being forced to stop it halfway through and mess with a bunch of wires and buttons before you can\n    run the rest of the tests.\n   \nAnd then you asked yourself, 'How do I write my tests so that the device doesn't need to be connected?' You may have gone down the rabbit hole that is mocking, and then replaced large parts of pyserial with mock interfaces, and ultimately ended up with a test suite that was significantly more complex than the program it was meant to test.\n\nWith pytest-reserial, you don't have to worry about any of that. Just write your tests as if the device is always connected. Then, simply use the `reserial` fixture to record the serial traffic from passing tests, and replay it when the device isn't connected.\n\n## Requirements\n\npytest-reserial depends on pytest and pyserial.\n\nIf jsbeautifier is installed, each test's recorded traffic is written on a single line in the log files. Otherwise, each recorded byte is written on a separate line.\n\n## Copyright\n\nMIT License, (C) 2022 Alexander Bessman\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Pytest fixture for recording and replaying serial port traffic.",
    "version": "0.3.0",
    "project_urls": {
        "Home": "https://github.com/bessman/pytest-reserial"
    },
    "split_keywords": [
        "serial",
        "testing",
        "logging",
        "mocking",
        "patching",
        "stubbing",
        "record",
        "replay"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "4c8edbb3b35a6f02613942fc7583565eeafd00127fbadc8f68a6f7a88caa31a9",
                "md5": "a95156a562d47e470d63704b7caa710e",
                "sha256": "eb0534d1b56ecb0c4470fb7b4e57bc0127a3fa293b4463f46068c3bc1dc5a708"
            },
            "downloads": -1,
            "filename": "pytest_reserial-0.3.0-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "a95156a562d47e470d63704b7caa710e",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 7209,
            "upload_time": "2024-02-08T09:05:44",
            "upload_time_iso_8601": "2024-02-08T09:05:44.622908Z",
            "url": "https://files.pythonhosted.org/packages/4c/8e/dbb3b35a6f02613942fc7583565eeafd00127fbadc8f68a6f7a88caa31a9/pytest_reserial-0.3.0-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "8c04c6c9d380db0823ca3db548bee469ee3ff1f737998273039c464955c3b7f3",
                "md5": "444600f1d47ee3de0ee69b61fc1bac8f",
                "sha256": "67a3da47da179e5dbebda2a9b0ea74f965c92d87d9f87a7e38d4a8406c113004"
            },
            "downloads": -1,
            "filename": "pytest_reserial-0.3.0.tar.gz",
            "has_sig": false,
            "md5_digest": "444600f1d47ee3de0ee69b61fc1bac8f",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 10844,
            "upload_time": "2024-02-08T09:05:48",
            "upload_time_iso_8601": "2024-02-08T09:05:48.461975Z",
            "url": "https://files.pythonhosted.org/packages/8c/04/c6c9d380db0823ca3db548bee469ee3ff1f737998273039c464955c3b7f3/pytest_reserial-0.3.0.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-02-08 09:05:48",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "bessman",
    "github_project": "pytest-reserial",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "pytest-reserial"
}
        
Elapsed time: 0.18383s