Package: pytest_robotframework
pytest-robotframeworkpytest-robotframeworkis a pytest plugin that generates robotframework reports for tests written
in python and allows you to run robotframework tests using pytestinstallpytest should automatically find and activate the plugin once you install it.API documentationclick herefeatureswrite robot tests in python# you can use both robot and pytest featuresfromrobot.apiimportloggerfrompytestimportCachefrompytest_robotframeworkimportkeyword@keyword# make this function show as a keyword in the robot logdeffoo():...@mark.slow# markers get converted to robot tagsdeftest_foo():foo()run.robotteststo allow for gradual adoption, the plugin also runs regular robot tests as well:*** Settings ***
test setup  foo

*** Test Cases ***
bar
    [Tags]  asdf  key:value
    no operation

*** Keywords ***
foo
    log  ran setupwhich is roughly equivalent to the following python code:# conftest.pyfromrobot.apiimportloggerfrompytest_robotframeworkimportkeyworddefpytest_runtet_setup():foo()@keyworddeffoo():logger.info("ran setup")# test_foo.pyfrompytestimportmark@mark.asdf@mark.key("value")deftest_bar():...setup/teardown and other hooksto define a function that runs for each test at setup or teardown, create aconftest.pywith apytest_runtest_setupand/orpytest_runtest_teardownfunction:# ./tests/conftest.pydefpytest_runtest_setup():log_in()# ./tests/test_suite.pydeftest_something():"""i am logged in now"""these hooks appear in the log the same way that the a.robotfile'sSetupandTeardownoptions in*** Settings ***would:for more information, seewriting hook functions. pretty much every pytest hook should work with this plugin
but i haven't tested them all. please raise an issue if you find one that's broken.tags/markerspytest markers are converted to tags in the robot log:frompytestimportmark@mark.slowdeftest_blazingly_fast_sorting_algorithm():[1,2,3].sort()markers likeskip,skipifandparameterizealso work how you'd expect:frompytestimportmark@mark.parametrize("test_input,expected",[(1,8),(6,6)])deftest_eval(test_input:int,expected:int):asserttest_input==expectedrobot suite variablesto set suite-level robot variables, call theset_variablesfunction at the top of the test suite:fromrobot.libraries.BuiltInimportBuiltInfrompytest_robotframeworkimportset_variablesset_variables({"foo":"bar","baz":["a","b"],})deftest_variables():assertBuiltIn().get_variable_value("$foo")=="bar"set_variablesis equivalent to the*** Variables ***section in a.robotfile. all variables are prefixed with$.@and&are not required since$variables can store lists and dicts anywayrunning tests in parallelrunning tests in parallel usingpytest-xdistis 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.configsince this is a pytest plugin, you should avoid using robot options that have pytest equivalents:instead of...use...notesrobot --include tag_namepytest -m tag_namerobot --exclude tag_namepytest -m not tag_namerobot --skip tag_namepytest -m "not tag_name"robot --test "test name" ./test.robotpytest ./test.robot::"Test Name"robot --suite "suite name" ./folderpytest ./folderrobot --dryrunpytest --collect-onlynot exactly the same. you should usea type checkeron your python tests as a replacement for robot dryrunrobot --exitonfailurepytest --maxfail=1robot --rerunfailedpytest --lfrobot --runemptysuitepytest --suppress-no-test-exit-coderequires thepytest-custom-exit-codepluginrobot --helppytest --helpall supported robot options will be listed in therobotframeworksectionspecifying robot options directltythere 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.pytest cli argumentsmost 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:beforerobot --loglevel DEBUG:INFO foo.robotafterpytest --robot-loglevel DEBUG:INFO test_foo.pyyou can see a complete list of the available arguments using thepytest --helpcommand. any robot arguments not present in that list are not supported because they are replaced by a pytest equivalent (see above).pytest_robot_modify_optionshookyou can specify apytest_robot_modify_optionshook in yourconftest.pyto programmatically modify the arguments. see thepytest_robotframework.hooksdocumentation for more information.frompytest_robotframeworkimportRobotOptionsfromrobot.api.interfacesimportListenerV3classFoo(ListenerV3):...defpytest_robot_modify_options(options:RobotOptions,session:Session)->None:ifnotsession.config.option.collectonly:options["loglevel"]="DEBUG:INFO"options["listener"].append(Foo())# you can specify instances as listeners, prerebotmodifiers, etc.note that not all arguments that the plugin passes to robot will be present in theargslist. arguments required for the plugin to function (eg. the plugin's listeners and prerunmodifiers) cannot be viewed or modified with this hookROBOT_OPTIONSenvironment variableROBOT_OPTIONS="-d results --listener foo.Foo"enabling pytest assertions in the robot logby default, only failed assertions will appear in the log. to make passed assertions show up, you'll have to addenable_assertion_pass_hook = trueto your pytest ini options:# pyproject.toml[tool.pytest.ini_options]enable_assertion_pass_hook=truehiding non-user facing assertionsyou may have existingassertstatements 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 individualassertstatements from the log:frompytest_robotframeworkimportAssertOptions,hide_asserts_from_robot_logdeftest_foo():# hide a single passing `assert` statement:assertfoo==bar,AssertOptions(log_pass=False)# hide a group of passing `assert` statements:withhide_asserts_from_robot_log():assertfoo==barassertbar==baznote that failingassertstatements will still show in the log regardless.you can also run pytest with the--no-assertions-in-robot-logargument to disableassertstatements in the robot log by default, then useAssertOptionsto explicitly enable individualassertstatements:frompytest_robotframeworkimportAssertOptionsdeftest_foo():assert"foo"=="bar"# hidden from the robot log (when run with --no-assertions-in-robot-log)assert"bar"=="baz",AssertOptions(log_pass=True)# not hiddencustomizing assertionspytest-robotframework allows you to customize the message for theassertkeyword which appears on both passing and failing assertions:assert1==1# no custom descriptionassert1==1,AssertOptions(description="custom description")you can still pass a custom message to be displayed only when your assertion fails:assert1==2,"the values did not match"however if you want to specify both a custom description and a failure message, you can use thefail_messageargument:assert1==2,"failure message"assert1==2,AssertOptions(description="checking values",fail_message="failure message")note thatenable_assertion_pass_hookpytest option needs to be enabled for this to work.limitations with tests written in pythonthere are some limitations when writing robotframework tests in python. pytest-robotframework includes solutions for these issues.making keywords show in the robot logby default when writing tests in python, the only keywords that you'll see in the robot log areSetup,Run TestandTeardown. this is because robot is not capable of recognizing keywords called outside of robot code. (seethis issue)this plugin has several workarounds for the problem:@keyworddecoratorif you want a function you wrote to show up as a keyword in the log, decorate it with thepytest_robotframework.keywordinstead ofrobot.api.deco.keywordfrompytest_robotframeworkimportkeyword@keyworddeffoo():...pytest functions are patched by the pluginmost of thepytest functionsare patched so that they show as keywords in the robot logdeftest_foo():withpytest.raises(ZeroDivisionError):logger.info(1/0)patching third party functions withkeywordifyif you want a function from a third party module/robot library to be displayed as a keyword, you can patch it with thekeywordifyfunction:# in your conftest.pyfrompyest_robotframeworkimportkeywordifyimportsome_module# patch a function from the module:keywordify(some_module,"some_function")# works on classes too:keywordify(some_module.SomeClass,"some_method")continuable failures don't workkeywords that raiseContinuableFailuredon't work properly when called from python code. this includes builtin keywords such asRun Keyword And Continue On Failure.usepytest.raisesfor expected failures instead:frompytestimportraiseswithraises(SomeException):some_keyword_that_fails()or if the exception is conditionally raised, use atry/exceptstatement like you would in regular python code:try:some_keyword_that_fails()exceptSomeException:...# ignore the exception, or re-raise it laterthe keyword will still show as failed in the log (as long as it's decorated withpytest_robotframework.keyword), but it won't effect the status of the test unless the exception is re-raised.why?robotframework introducedTRY/EXCEPTstatements in version 5.0, which theynow recommend usinginstead of the oldRun Keyword And Ignore Error/Run Keyword And Expect Errorkeywords.howeverTRY/EXCEPTbehaves differently to its python equivalent, as it allows for errors that do not actually raise an exception to be caught:*** Test Cases ***
Foo
    TRY
        Run Keyword And Continue On Failure    Fail
        Log    this is executed
    EXCEPT
        Log    and so is this
    ENDthis means that if control flows likeRun Keyword And Continue On Failurewere supported, its failures would be impossible to catch:fromrobot.api.loggerimportinfofromrobot.libraries.BuiltInimportBuiltIntry:BuiltIn().run_keyword_and_continue_on_failure("fail")info("this is executed because an exception was not actually raised")except:info("this is NOT executed, but the test will still fail")IDE integrationvscodevscode's builtin python plugin should discover both your python and robot tests by default, and show run buttons next to them:if you use therobotframework-lspextension, you'll see duplicated tests on.robotfiles because you have two extensions that can discover them. to fix this, setrobot.testView.enabledtofalsein vscode's settings.(note: at the time of writing, this option is not yet in the latest version of the extension, so for now you'll need to installthis build)pycharmpycharm currently does not support pytest plugins for non-python files. seethis issuecompatibilitydependencyversion rangecommentspython>=3.8,<4.0all versions of python will be supported until their end-of-life as describedhererobotframework>=6.1,<8.0i 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.1pytest>=7.0,<9.0may work on other versions, but things may break since this plugin relies on some internal pytest modules