{"id":13772101,"url":"https://github.com/detachhead/pytest-robotframework","last_synced_at":"2025-04-09T08:09:31.203Z","repository":{"id":186971897,"uuid":"675983742","full_name":"DetachHead/pytest-robotframework","owner":"DetachHead","description":"a pytest plugin that creates robotframework reports for tests written in python and allows you to run robotframework tests with pytest","archived":false,"fork":false,"pushed_at":"2024-11-13T04:34:44.000Z","size":1082,"stargazers_count":30,"open_issues_count":28,"forks_count":3,"subscribers_count":4,"default_branch":"master","last_synced_at":"2024-11-25T09:16:07.153Z","etag":null,"topics":["pytest","python","robotframework"],"latest_commit_sha":null,"homepage":"https://detachhead.github.io/pytest-robotframework/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DetachHead.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-08-08T07:07:19.000Z","updated_at":"2024-11-13T04:34:47.000Z","dependencies_parsed_at":"2024-01-27T07:34:48.791Z","dependency_job_id":"56e003a3-3c68-4ec0-92c8-717dbba8598f","html_url":"https://github.com/DetachHead/pytest-robotframework","commit_stats":{"total_commits":655,"total_committers":5,"mean_commits":131.0,"dds":"0.14503816793893132","last_synced_commit":"2e4f456e5228796a4ee8415227291990106704b3"},"previous_names":["detachhead/pytest-robotframework"],"tags_count":55,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DetachHead%2Fpytest-robotframework","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DetachHead%2Fpytest-robotframework/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DetachHead%2Fpytest-robotframework/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DetachHead%2Fpytest-robotframework/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DetachHead","download_url":"https://codeload.github.com/DetachHead/pytest-robotframework/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247999861,"owners_count":21031046,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["pytest","python","robotframework"],"created_at":"2024-08-03T17:00:59.991Z","updated_at":"2025-04-09T08:09:31.184Z","avatar_url":"https://github.com/DetachHead.png","language":"Python","funding_links":[],"categories":["Plugins"],"sub_categories":[],"readme":"\u003c!-- hidden attributes used on elements we only want to hide on the docs site, since they're ignored by github's markdown viewer --\u003e\n\n\u003ch1 hidden\u003epytest-robotframework\u003c/h1\u003e\n\n`pytest-robotframework` is a pytest plugin that creates robotframework reports for tests written\nin python and allows you to run robotframework tests with pytest.\n\n![image](https://github.com/DetachHead/pytest-robotframework/assets/57028336/bf0e787c-6343-4ad1-89ff-44b93e17f7f5)\n\n# install\n\n[![Stable Version](https://img.shields.io/pypi/v/pytest-robotframework?color=blue)](https://pypi.org/project/pytest-robotframework/)\n[![Conda Version](https://img.shields.io/conda/vn/conda-forge/pytest-robotframework.svg)](https://anaconda.org/conda-forge/pytest-robotframework)\n\npytest should automatically find and activate the plugin once you install it.\n\n\u003ch1 hidden\u003eAPI documentation\u003c/h1\u003e\n\n\u003cb hidden\u003e\u003ca href=\"https://detachhead.github.io/pytest-robotframework/pytest_robotframework.html#api\"\u003eclick here\u003c/a\u003e\u003c/b\u003e\n\n# features\n\n## write robot tests in python\n\n```py\n# you can use both robot and pytest features\nfrom robot.api import logger\nfrom pytest import Cache\n\nfrom pytest_robotframework import keyword\n\n@keyword  # make this function show as a keyword in the robot log\ndef foo():\n    ...\n\n@mark.slow  # markers get converted to robot tags\ndef test_foo():\n    foo()\n```\n\n## run `.robot` tests\n\nto allow for gradual adoption, the plugin also runs regular robot tests as well:\n\n```robot\n*** Settings ***\ntest setup  foo\n\n*** Test Cases ***\nbar\n    [Tags]  asdf  key:value\n    no operation\n\n*** Keywords ***\nfoo\n    log  ran setup\n```\n\nwhich is roughly equivalent to the following python code:\n\n```py\n# test_foo.py\nfrom pytest import mark\n\n@keyword\ndef foo():\n    logger.info(\"ran setup\")\n\n@fixture(autouse=True)\ndef setup():\n    foo()\n\n@mark.asdf\n@mark.key(\"value\")\ndef test_bar():\n    ...\n```\n\n## setup/teardown\n\nin pytest, setups and teardowns are defined using fixtures:\n\n```py\nfrom pytest import fixture\nfrom robot.api import logger\n\n@fixture\ndef user():\n    logger.info(\"logging in\")\n    user = ...\n    yield user\n    logger.info(\"logging off\")\n\ndef test_something(user):\n    ...\n```\n\nunder the hood, pytest calls the fixture setup/teardown code as part of the `pytest_runtest_setup` and and `pytest_runtest_teardown` hooks, which appear in the robot log like so:\n\n![image](https://github.com/DetachHead/pytest-robotframework/assets/57028336/5583883e-c9a5-47a0-8796-63418973909d)\n\nfor more information, see the pytest documentation for [fixtures](https://docs.pytest.org/en/6.2.x/fixture.html) and [hook functions](https://docs.pytest.org/en/7.1.x/how-to/writing_hook_functions.html).\n\n## tags/markers\n\npytest markers are converted to tags in the robot log:\n\n```py\nfrom pytest import mark\n\n@mark.slow\ndef test_blazingly_fast_sorting_algorithm():\n    [1,2,3].sort()\n```\n\n![](https://github.com/DetachHead/pytest-robotframework/assets/57028336/f25ee4bd-2f10-42b4-bdef-18a22379bd0d)\n\nmarkers like `skip`, `skipif` and `parameterize` also work how you'd expect:\n\n```py\nfrom pytest import mark\n\n@mark.parametrize(\"test_input,expected\", [(1, 8), (6, 6)])\ndef test_eval(test_input: int, expected: int):\n    assert test_input == expected\n```\n\n![image](https://github.com/DetachHead/pytest-robotframework/assets/57028336/4361295b-5e44-4c9d-b2f3-839e3901b1eb)\n\n## robot suite variables\n\nto set suite-level robot variables, call the `set_variables` function at the top of the test suite:\n\n```py\nfrom robot.libraries.BuiltIn import BuiltIn\nfrom pytest_robotframework import set_variables\n\nset_variables(\n    {\n        \"foo\": \"bar\",\n        \"baz\": [\"a\", \"b\"],\n    }\n)\n\ndef test_variables():\n    assert BuiltIn().get_variable_value(\"$foo\") == \"bar\"\n```\n\n`set_variables` is equivalent to the `*** Variables ***` section in a `.robot` file. all variables are prefixed with `$`. `@` and `\u0026` are not required since `$` variables can store lists and dicts anyway\n\n## running tests in parallel\n\nrunning tests in parallel using [pytest-xdist](https://pytest-xdist.readthedocs.io/en/stable/) is supported. when running with xdist, pytest-robotframework will run separate instances of robot for each test, then merge the robot output files together automatically using rebot.\n\n# config\n\npass `--capture=no` to make `logger.console` work properly.\n\nsince this is a pytest plugin, you should avoid using robot options that have pytest equivalents:\n\n| instead of...                           | use...                                | notes                                                                                                                                                   |\n| :-------------------------------------- | :------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `robot --include tag_name`              | `pytest -m tag_name`                  |                                                                                                                                                         |\n| `robot --exclude tag_name`              | `pytest -m not tag_name`              |                                                                                                                                                         |\n| `robot --skip tag_name`                 | `pytest -m \"not tag_name\"`            |                                                                                                                                                         |\n| `robot --test \"test name\" ./test.robot` | `pytest ./test.robot::\"Test Name\"`    |                                                                                                                                                         |\n| `robot --suite \"suite name\" ./folder`   | `pytest ./folder`                     |                                                                                                                                                         |\n| `robot --dryrun`                        | `pytest --collect-only`               | not exactly the same. you should use [a type checker](https://github.com/kotlinisland/basedmypy) on your python tests as a replacement for robot dryrun |\n| `robot --exitonfailure`                 | `pytest --maxfail=1`                  |                                                                                                                                                         |\n| `robot --rerunfailed`                   | `pytest --lf`                         |                                                                                                                                                         |\n| `robot --runemptysuite`                 | `pytest --suppress-no-test-exit-code` | requires the [pytest-custom-exit-code](https://pypi.org/project/pytest-custom-exit-code/) plugin                                                        |\n| `robot --help`                          | `pytest --help`                       | all supported robot options will be listed in the `robotframework` section                                                                              |\n\n## specifying robot options directlty\n\nthere are multiple ways you can specify the robot arguments directly. however, arguments that have pytest equivalents cannot be set with robot as they would cause the plugin to behave incorrectly.\n\n### pytest cli arguments\n\nmost robot cli arguments can be passed to pytest by prefixing the argument names with `--robot-`. for example, here's how to change the log level:\n\n#### before\n\n```\nrobot --loglevel DEBUG:INFO foo.robot\n```\n\n#### after\n\n```\npytest --robot-loglevel DEBUG:INFO test_foo.py\n```\n\nyou can see a complete list of the available arguments using the `pytest --help` command. any robot arguments not present in that list are not supported because they are replaced by a pytest equivalent ([see above](#config)).\n\n### `pytest_robot_modify_options` hook\n\nyou can specify a `pytest_robot_modify_options` hook in your `conftest.py` to programmatically modify the arguments. see the [pytest_robotframework.hooks](http://detachhead.github.io/pytest-robotframework/pytest_robotframework/hooks.html#pytest_robot_modify_options) documentation for more information.\n\n```py\nfrom pytest_robotframework import RobotOptions\nfrom robot.api.interfaces import ListenerV3\n\nclass Foo(ListenerV3):\n    ...\n\ndef pytest_robot_modify_options(options: RobotOptions, session: Session) -\u003e None:\n    if not session.config.option.collectonly:\n        options[\"loglevel\"] = \"DEBUG:INFO\"\n        options[\"listener\"].append(Foo()) # you can specify instances as listeners, prerebotmodifiers, etc.\n```\n\nnote that not all arguments that the plugin passes to robot will be present in the `args` list. arguments required for the plugin to function (eg. the plugin's listeners and prerunmodifiers) cannot be viewed or modified with this hook\n\n### `ROBOT_OPTIONS` environment variable\n\n```\nROBOT_OPTIONS=\"-d results --listener foo.Foo\"\n```\n\n## enabling pytest assertions in the robot log\n\nby default, only failed assertions will appear in the log. to make passed assertions show up, you'll have to add `enable_assertion_pass_hook = true` to your pytest ini options:\n\n```toml\n# pyproject.toml\n[tool.pytest.ini_options]\nenable_assertion_pass_hook = true\n```\n\n![image](https://github.com/DetachHead/pytest-robotframework/assets/57028336/c2525ccf-c1c6-4c06-be79-c36fefd3bed4)\n\n### hiding non-user facing assertions\n\nyou may have existing `assert` statements in your codebase that are not intended to be part of your tests (eg. for narrowing types/validating input data) and don't want them to show up in the robot log. there are two ways you can can hide individual `assert` statements from the log:\n\n```py\nfrom pytest_robotframework import AssertOptions, hide_asserts_from_robot_log\n\ndef test_foo():\n    # hide a single passing `assert` statement:\n    assert foo == bar, AssertOptions(log_pass=False)\n\n    # hide a group of passing `assert` statements:\n    with hide_asserts_from_robot_log():\n        assert foo == bar\n        assert bar == baz\n```\n\nnote that failing `assert` statements will still show in the log regardless.\n\nyou can also run pytest with the `--no-assertions-in-robot-log` argument to disable `assert` statements in the robot log by default, then use `AssertOptions` to explicitly enable individual `assert` statements:\n\n```py\nfrom pytest_robotframework import AssertOptions\n\ndef test_foo():\n    assert \"foo\" == \"bar\" # hidden from the robot log (when run with --no-assertions-in-robot-log)\n    assert \"bar\" == \"baz\", AssertOptions(log_pass=True) # not hidden\n```\n\n### customizing assertions\n\npytest-robotframework allows you to customize the message for the `assert` keyword which appears on both passing and failing assertions:\n\n```py\nassert 1 == 1  # no custom description\nassert 1 == 1, AssertOptions(description=\"custom description\")\n```\n\n![image](https://github.com/DetachHead/pytest-robotframework/assets/57028336/582f3589-0ba2-469d-916d-8945e4feffbb)\n\nyou can still pass a custom message to be displayed only when your assertion fails:\n\n```py\nassert 1 == 2, \"the values did not match\"\n```\n\nhowever if you want to specify both a custom description and a failure message, you can use the `fail_message` argument:\n\n```py\nassert 1 == 2, \"failure message\"\nassert 1 == 2, AssertOptions(description=\"checking values\", fail_message=\"failure message\")\n```\n\n![image](https://github.com/DetachHead/pytest-robotframework/assets/57028336/5bae04a0-0545-4df8-9637-59f8f1ef2f04)\n\nnote that `enable_assertion_pass_hook` pytest option needs to be enabled for this to work.\n\n# limitations with tests written in python\n\nthere are some limitations when writing robotframework tests in python. pytest-robotframework includes solutions for these issues.\n\n## making keywords show in the robot log\n\nby default when writing tests in python, the only keywords that you'll see in the robot log are `Setup`, `Run Test` and `Teardown`. this is because robot is not capable of recognizing keywords called outside of robot code. (see [this issue](https://github.com/robotframework/robotframework/issues/4252))\n\nthis plugin has several workarounds for the problem:\n\n### `@keyword` decorator\n\nif you want a function you wrote to show up as a keyword in the log, decorate it with the `pytest_robotframework.keyword` instead of `robot.api.deco.keyword`\n\n```py\nfrom pytest_robotframework import keyword\n\n@keyword\ndef foo():\n    ...\n```\n\n### pytest functions are patched by the plugin\n\nmost of the [pytest functions](https://docs.pytest.org/en/7.1.x/reference/reference.html#functions) are patched so that they show as keywords in the robot log\n\n```py\ndef test_foo():\n    with pytest.raises(ZeroDivisionError):\n        logger.info(1 / 0)\n```\n\n![image](https://github.com/DetachHead/pytest-robotframework/assets/57028336/fc15e9a9-578d-4c5d-bc0f-d5d68591c66c)\n\n#### patching third party functions with `keywordify`\n\nif you want a function from a third party module/robot library to be displayed as a keyword, you can patch it with the `keywordify` function:\n\n```py\n# in your conftest.py\n\nfrom pyest_robotframework import keywordify\nimport some_module\n\n# patch a function from the module:\nkeywordify(some_module, \"some_function\")\n# works on classes too:\nkeywordify(some_module.SomeClass, \"some_method\")\n```\n\n## continuable failures don't work\n\nkeywords that raise [`ContinuableFailure`](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#continuable-failures) don't work properly when called from python code. this includes builtin keywords such as `Run Keyword And Continue On Failure`.\n\nuse `pytest.raises` for expected failures instead:\n\n```py\nfrom pytest import raises\n\nwith raises(SomeException):\n    some_keyword_that_fails()\n```\n\nor if the exception is conditionally raised, use a `try`/`except` statement like you would in regular python code:\n\n```py\ntry:\n    some_keyword_that_fails()\nexcept SomeException:\n    ... # ignore the exception, or re-raise it later\n```\n\nthe keyword will still show as failed in the log (as long as it's decorated with `pytest_robotframework.keyword`), but it won't effect the status of the test unless the exception is re-raised.\n\n### why?\n\nrobotframework introduced `TRY`/`EXCEPT` statements in version 5.0, which they [now recommend using](https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#other-ways-to-handle-errors) instead of the old `Run Keyword And Ignore Error`/`Run Keyword And Expect Error` keywords.\n\nhowever `TRY`/`EXCEPT` behaves differently to its python equivalent, as it allows for errors that do not actually raise an exception to be caught:\n\n```robot\n*** Test Cases ***\nFoo\n    TRY\n        Run Keyword And Continue On Failure    Fail\n        Log    this is executed\n    EXCEPT\n        Log    and so is this\n    END\n```\n\nthis means that if control flows like `Run Keyword And Continue On Failure` were supported, its failures would be impossible to catch:\n\n```py\nfrom robot.api.logger import info\nfrom robot.libraries.BuiltIn import BuiltIn\n\ntry:\n    BuiltIn().run_keyword_and_continue_on_failure(\"fail\")\n    info(\"this is executed because an exception was not actually raised\")\nexcept:\n    info(\"this is NOT executed, but the test will still fail\")\n```\n\n# IDE integration\n\n## vscode\n\nvscode's builtin python plugin should discover both your python and robot tests by default, and show run buttons next to them:\n\n![image](https://github.com/DetachHead/pytest-robotframework/assets/57028336/d81278cc-1574-4360-be3c-29805b47dec6)\n![image](https://github.com/DetachHead/pytest-robotframework/assets/57028336/cce2fc08-806f-4b0e-85b9-42be677871ab)\n\n### running `.robot` tests\n\nif you still intend to use `.robot` files with pytest-robotframework, we recommend using the [robotcode](https://github.com/d-biehl/robotcode) extension and setting `robotcode.testExplorer.enabled` to `false` in `.vscode/settings.json`. this will prevent the tests from being duplicated in the test explorer.\n\n## pycharm\n\npycharm currently does not support pytest plugins for non-python files. see [this issue](https://youtrack.jetbrains.com/issue/PY-63110/use-pytest-collect-only-to-collect-pytest-tests)\n\n# compatibility\n\n| dependency     | version range | comments                                                                                                                                                                                                   |\n| :------------- | :------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| python         | `\u003e=3.9,\u003c4.0`  | all versions of python will be supported until their end-of-life as described [here](https://devguide.python.org/versions/)                                                                                |\n| robotframework | `\u003e=6.1,\u003c8.0`  | i will try to support at least the two most recent major versions. robot 6.0 is not supported as the parser API that the plugin relies on to support tests written in python was introduced in version 6.1 |\n| pytest         | `\u003e=7.0,\u003c9.0`  | may work on other versions, but things may break since this plugin relies on some internal pytest modules                                                                                                  |\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdetachhead%2Fpytest-robotframework","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdetachhead%2Fpytest-robotframework","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdetachhead%2Fpytest-robotframework/lists"}