From 4b663475c39d01578b51e3905aa3c769e48d0717 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Thu, 2 Feb 2023 01:00:18 +0700 Subject: [PATCH 01/18] Move to homogeneus testing environment: pytest,pytest-bdd,behave(p) --- .../examples/description/description.feature | 15 ++ .../examples/description/environment.py | 14 ++ .../description/steps/description_steps.py | 12 ++ .../{features => examples}/hook.feature | 0 .../{features => examples}/label.feature | 0 .../{features => examples}/link.feature | 0 .../{features => examples}/no_skipped.feature | 0 .../{features => examples}/scenario.feature | 0 .../scenario_outline.feature | 0 .../{features => examples}/severity.feature | 0 .../{features => examples}/step.feature | 0 .../steps/behave_steps.py | 0 .../steps/dummy_steps.py | 1 + .../steps/report_steps.py | 0 .../{features => examples}/tag.feature | 0 .../{features => examples}/test_plan.feature | 0 .../{features => examples}/unicode.feature | 0 allure-behave/features/background.feature | 43 ----- allure-behave/features/description.feature | 36 ----- .../examples/scenario-outline/outline.feature | 10 ++ .../scenario-outline/scenario_outline_test.py | 22 +++ .../examples/simple-scenario/scenario.feature | 5 + .../examples/simple-scenario/scenario_test.py | 21 +++ allure-pytest-bdd/features/background.feature | 1 - allure-pytest-bdd/features/outline.feature | 51 ------ allure-pytest-bdd/features/scenario.feature | 32 ---- allure-pytest-bdd/setup.py | 1 + allure-pytest-bdd/test/conftest.py | 63 -------- allure-pytest-bdd/test/outline_test.py | 6 - allure-pytest-bdd/test/scenario_test.py | 6 - allure-pytest-bdd/test/steps.py | 57 ------- .../label/package/regression_test.py | 41 ----- .../test/acceptance/link/link_pattern_test.py | 22 --- .../acceptance/step/step_placeholder_test.py | 51 ------ allure-pytest/test/conftest.py | 80 ---------- .../select_test_from_testplan_test.py | 102 ------------ pyproject.toml | 8 + {allure-pytest-bdd/test => tests}/__init__.py | 0 .../acceptance/background/background.feature | 11 ++ .../acceptance/background/background_steps.py | 23 +++ .../acceptance/background/background_test.py | 70 ++++++++ .../description/description_test.py | 52 ++++++ tests/allure_behave/conftest.py | 93 +++++++++++ .../test => tests/allure_pytest}/__init__.py | 0 .../allure_pytest}/acceptance/__init__.py | 0 .../acceptance/attachment/__init__.py | 0 .../attachment/attachment_class_test.py | 2 +- .../attachment/attachment_fixture_test.py | 2 +- .../attachment/attachment_hook_test.py | 0 .../attachment_parametrized_test.py | 0 .../attachment/attachment_step_test.py | 2 +- .../acceptance/attachment/attachment_test.py | 2 +- .../acceptance/capture/__init__.py | 0 .../acceptance/capture/capture_attach_test.py | 0 .../acceptance/description/__init__.py | 0 .../description/description_test.py | 2 +- .../description/dynamic_description_test.py | 2 +- .../acceptance/display_name/__init__.py | 0 .../display_name/display_name_test.py | 2 +- .../display_name/dynamic_display_name_test.py | 4 +- .../acceptance/duration/__init__.py | 0 .../acceptance/duration/duration_time_test.py | 0 .../acceptance/fixture/__init__.py | 0 .../fixture/fixture_finalized_test.py | 0 .../acceptance/fixture/fixture_test.py | 0 .../fixture/function_scope/__init__.py | 0 .../fixture/parametrized_fixture_test.py | 0 .../acceptance/fixture/yield_fixture_test.py | 0 .../acceptance/history_id/__init__.py | 0 .../acceptance/history_id/history_id_test.py | 0 .../acceptance/label/__init__.py | 0 .../acceptance/label/bdd/__init__.py | 0 .../acceptance/label/bdd/bdd_label_test.py | 2 +- .../label/bdd/dynamic_bdd_label_test.py | 2 +- .../acceptance/label/bdd/select_bdd_test.py | 2 +- .../acceptance/label/custom/__init__.py | 0 .../label/custom/custom_label_test.py | 2 +- .../label/custom/select_custom_label_test.py | 2 +- .../label/id}/set_testcase_id_test.py | 0 .../acceptance/label/manual/__init__.py | 0 .../acceptance/label/manual/manual_test.py | 4 +- .../acceptance/label/package/__init__.py | 0 .../label/package/regression_test.py | 48 ++++++ .../acceptance/label/severity/__init__.py | 0 .../label/severity/class_severity_test.py | 2 +- .../label/severity/module_severity_test.py | 2 +- .../label/severity/select_severity_test.py | 2 +- .../label/severity/severity_test.py | 4 +- .../acceptance/label/suite/__init__.py | 0 .../acceptance/label/suite/custom_suite.py | 2 +- .../label/suite/default_suite_test.py | 0 .../suite/module_level_custom_suite_test.py | 2 +- .../acceptance/label/tag/__init__.py | 0 .../acceptance/label/tag/tag_test.py | 0 .../acceptance/link/__init__.py | 0 .../acceptance/link/dynamic_link_test.py | 2 +- .../acceptance/link/link_pattern_test.py | 26 +++ .../acceptance/link/link_test.py | 2 +- .../acceptance/parametrization/__init__.py | 0 .../parametrization/metafunc_test.py | 0 .../parametrization/parametrization_test.py | 0 .../acceptance/status/__init__.py | 0 .../status/base_call_status_test.py | 0 .../status/base_setup_status_test.py | 0 .../status/base_step_status_test.py | 0 .../status/base_teardown_status_test.py | 0 .../status/skip_call_status_test.py | 0 .../status/skip_setup_status_test.py | 0 .../status/skip_step_status_test.py | 0 .../status/skip_teardown_status_test.py | 0 .../status/xfail_call_status_test.py | 0 .../status/xfail_setup_status_test.py | 0 .../status/xfail_step_status_test.py | 0 .../status/xfail_teardown_status_test.py | 0 .../acceptance/step/__init__.py | 0 .../acceptance/step/outside_step_test.py | 0 .../acceptance/step/step_parameters.py | 0 .../acceptance/step/step_placeholder_test.py | 59 +++++++ .../acceptance/step/step_test.py | 2 +- ...st_step_with_several_step_inside_thread.py | 0 .../select_test_from_testplan_test.py | 115 ++++++++++++++ .../acceptance/unicode_identifier/__init__.py | 0 .../unicode_identifier_test.py | 0 .../external_packages_support}/__init__.py | 0 .../pytest_check}/__init__.py | 0 .../pytest_check/pytest_check_test.py | 1 - .../pytest_doctest}/__init__.py | 0 .../pytest_doctest/pytest_doctest_test.py | 0 .../pytest_flakes}/__init__.py | 0 .../pytest_flakes/pytest_flakes_test.py | 0 .../pytest_lazy_fixture}/__init__.py | 0 .../pytest_lazy_fixture_test.py | 0 .../pytest_pluginmanager}/__init__.py | 0 .../pytest_get_allure_plugin_test.py | 0 .../pytest_rerunfailures}/__init__.py | 0 .../pytest_rerunfailures_test.py | 0 .../pytest_xdist}/__init__.py | 0 .../pytest_xdist/pytest_xdist_select_test.py | 0 .../allure_pytest_bdd}/__init__.py | 0 .../allure_pytest_bdd/acceptance/__init__.py | 0 .../acceptance/scenario_outline_test.py | 33 ++++ .../acceptance/scenario_test.py | 21 +++ tests/allure_pytest_bdd/conftest.py | 7 + tests/conftest.py | 149 ++++++++++++++++++ 144 files changed, 842 insertions(+), 618 deletions(-) create mode 100644 allure-behave/examples/description/description.feature create mode 100644 allure-behave/examples/description/environment.py create mode 100644 allure-behave/examples/description/steps/description_steps.py rename allure-behave/{features => examples}/hook.feature (100%) rename allure-behave/{features => examples}/label.feature (100%) rename allure-behave/{features => examples}/link.feature (100%) rename allure-behave/{features => examples}/no_skipped.feature (100%) rename allure-behave/{features => examples}/scenario.feature (100%) rename allure-behave/{features => examples}/scenario_outline.feature (100%) rename allure-behave/{features => examples}/severity.feature (100%) rename allure-behave/{features => examples}/step.feature (100%) rename allure-behave/{features => examples}/steps/behave_steps.py (100%) rename allure-behave/{features => examples}/steps/dummy_steps.py (97%) rename allure-behave/{features => examples}/steps/report_steps.py (100%) rename allure-behave/{features => examples}/tag.feature (100%) rename allure-behave/{features => examples}/test_plan.feature (100%) rename allure-behave/{features => examples}/unicode.feature (100%) delete mode 100644 allure-behave/features/background.feature delete mode 100644 allure-behave/features/description.feature create mode 100644 allure-pytest-bdd/examples/scenario-outline/outline.feature create mode 100644 allure-pytest-bdd/examples/scenario-outline/scenario_outline_test.py create mode 100644 allure-pytest-bdd/examples/simple-scenario/scenario.feature create mode 100644 allure-pytest-bdd/examples/simple-scenario/scenario_test.py delete mode 100644 allure-pytest-bdd/features/background.feature delete mode 100644 allure-pytest-bdd/features/outline.feature delete mode 100644 allure-pytest-bdd/features/scenario.feature delete mode 100644 allure-pytest-bdd/test/conftest.py delete mode 100644 allure-pytest-bdd/test/outline_test.py delete mode 100644 allure-pytest-bdd/test/scenario_test.py delete mode 100644 allure-pytest-bdd/test/steps.py delete mode 100644 allure-pytest/test/acceptance/label/package/regression_test.py delete mode 100644 allure-pytest/test/acceptance/link/link_pattern_test.py delete mode 100644 allure-pytest/test/acceptance/step/step_placeholder_test.py delete mode 100644 allure-pytest/test/conftest.py delete mode 100644 allure-pytest/test/integration/allure_ee/select_test_from_testplan_test.py create mode 100644 pyproject.toml rename {allure-pytest-bdd/test => tests}/__init__.py (100%) create mode 100644 tests/allure_behave/acceptance/background/background.feature create mode 100644 tests/allure_behave/acceptance/background/background_steps.py create mode 100644 tests/allure_behave/acceptance/background/background_test.py create mode 100644 tests/allure_behave/acceptance/description/description_test.py create mode 100644 tests/allure_behave/conftest.py rename {allure-pytest/test => tests/allure_pytest}/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/attachment/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/attachment/attachment_class_test.py (88%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/attachment/attachment_fixture_test.py (95%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/attachment/attachment_hook_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/attachment/attachment_parametrized_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/attachment/attachment_step_test.py (96%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/attachment/attachment_test.py (94%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/capture/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/capture/capture_attach_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/description/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/description/description_test.py (95%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/description/dynamic_description_test.py (92%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/display_name/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/display_name/display_name_test.py (98%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/display_name/dynamic_display_name_test.py (85%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/duration/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/duration/duration_time_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/fixture/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/fixture/fixture_finalized_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/fixture/fixture_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/fixture/function_scope/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/fixture/parametrized_fixture_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/fixture/yield_fixture_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/history_id/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/history_id/history_id_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/bdd/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/bdd/bdd_label_test.py (95%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/bdd/dynamic_bdd_label_test.py (96%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/bdd/select_bdd_test.py (95%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/custom/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/custom/custom_label_test.py (88%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/custom/select_custom_label_test.py (95%) rename {allure-pytest/test/integration/allure_ee => tests/allure_pytest/acceptance/label/id}/set_testcase_id_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/manual/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/manual/manual_test.py (86%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/package/__init__.py (100%) create mode 100644 tests/allure_pytest/acceptance/label/package/regression_test.py rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/severity/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/severity/class_severity_test.py (96%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/severity/module_severity_test.py (94%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/severity/select_severity_test.py (94%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/severity/severity_test.py (86%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/suite/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/suite/custom_suite.py (90%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/suite/default_suite_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/suite/module_level_custom_suite_test.py (85%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/tag/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/label/tag/tag_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/link/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/link/dynamic_link_test.py (96%) create mode 100644 tests/allure_pytest/acceptance/link/link_pattern_test.py rename {allure-pytest/test => tests/allure_pytest}/acceptance/link/link_test.py (96%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/parametrization/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/parametrization/metafunc_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/parametrization/parametrization_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/status/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/status/base_call_status_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/status/base_setup_status_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/status/base_step_status_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/status/base_teardown_status_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/status/skip_call_status_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/status/skip_setup_status_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/status/skip_step_status_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/status/skip_teardown_status_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/status/xfail_call_status_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/status/xfail_setup_status_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/status/xfail_step_status_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/status/xfail_teardown_status_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/step/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/step/outside_step_test.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/step/step_parameters.py (100%) create mode 100644 tests/allure_pytest/acceptance/step/step_placeholder_test.py rename {allure-pytest/test => tests/allure_pytest}/acceptance/step/step_test.py (96%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/step/test_step_with_several_step_inside_thread.py (100%) create mode 100644 tests/allure_pytest/acceptance/testplan/select_test_from_testplan_test.py rename {allure-pytest/test => tests/allure_pytest}/acceptance/unicode_identifier/__init__.py (100%) rename {allure-pytest/test => tests/allure_pytest}/acceptance/unicode_identifier/unicode_identifier_test.py (100%) rename {allure-pytest/test/integration => tests/allure_pytest/external_packages_support}/__init__.py (100%) rename {allure-pytest/test/integration/allure_ee => tests/allure_pytest/external_packages_support/pytest_check}/__init__.py (100%) rename {allure-pytest/test/integration => tests/allure_pytest/external_packages_support}/pytest_check/pytest_check_test.py (99%) rename {allure-pytest/test/integration/pytest_check => tests/allure_pytest/external_packages_support/pytest_doctest}/__init__.py (100%) rename {allure-pytest/test/integration => tests/allure_pytest/external_packages_support}/pytest_doctest/pytest_doctest_test.py (100%) rename {allure-pytest/test/integration/pytest_doctest => tests/allure_pytest/external_packages_support/pytest_flakes}/__init__.py (100%) rename {allure-pytest/test/integration => tests/allure_pytest/external_packages_support}/pytest_flakes/pytest_flakes_test.py (100%) rename {allure-pytest/test/integration/pytest_flakes => tests/allure_pytest/external_packages_support/pytest_lazy_fixture}/__init__.py (100%) rename {allure-pytest/test/integration => tests/allure_pytest/external_packages_support}/pytest_lazy_fixture/pytest_lazy_fixture_test.py (100%) rename {allure-pytest/test/integration/pytest_lazy_fixture => tests/allure_pytest/external_packages_support/pytest_pluginmanager}/__init__.py (100%) rename {allure-pytest/test/integration => tests/allure_pytest/external_packages_support}/pytest_pluginmanager/pytest_get_allure_plugin_test.py (100%) rename {allure-pytest/test/integration/pytest_pluginmanager => tests/allure_pytest/external_packages_support/pytest_rerunfailures}/__init__.py (100%) rename {allure-pytest/test/integration => tests/allure_pytest/external_packages_support}/pytest_rerunfailures/pytest_rerunfailures_test.py (100%) rename {allure-pytest/test/integration/pytest_rerunfailures => tests/allure_pytest/external_packages_support/pytest_xdist}/__init__.py (100%) rename {allure-pytest/test/integration => tests/allure_pytest/external_packages_support}/pytest_xdist/pytest_xdist_select_test.py (100%) rename {allure-pytest/test/integration/pytest_xdist => tests/allure_pytest_bdd}/__init__.py (100%) create mode 100644 tests/allure_pytest_bdd/acceptance/__init__.py create mode 100644 tests/allure_pytest_bdd/acceptance/scenario_outline_test.py create mode 100644 tests/allure_pytest_bdd/acceptance/scenario_test.py create mode 100644 tests/allure_pytest_bdd/conftest.py create mode 100644 tests/conftest.py diff --git a/allure-behave/examples/description/description.feature b/allure-behave/examples/description/description.feature new file mode 100644 index 00000000..de6be3c7 --- /dev/null +++ b/allure-behave/examples/description/description.feature @@ -0,0 +1,15 @@ +Feature: Allure description for behave tests + Scenario: Description from a .feature file + This scenario has a description. + This description spans across multiple lines. + + Given step that passes + + Scenario: Description from a step definition function + Given step which adds a dynamic description to the scenario + + Scenario: Description from before_scenario hook + Given step that passes + + Scenario: Description from after_scenario hook + Given step that passes diff --git a/allure-behave/examples/description/environment.py b/allure-behave/examples/description/environment.py new file mode 100644 index 00000000..cfde9767 --- /dev/null +++ b/allure-behave/examples/description/environment.py @@ -0,0 +1,14 @@ +import allure + +def before_scenario(_, scenario): + if "before_scenario" in scenario.name: + allure.dynamic.description( + "This scenario has a description specified in before_scenario hook" + ) + + +def after_scenario(_, scenario): + if "after_scenario" in scenario.name: + allure.dynamic.description( + "This scenario has a description specified in after_scenario hook" + ) diff --git a/allure-behave/examples/description/steps/description_steps.py b/allure-behave/examples/description/steps/description_steps.py new file mode 100644 index 00000000..b812f783 --- /dev/null +++ b/allure-behave/examples/description/steps/description_steps.py @@ -0,0 +1,12 @@ +import allure +from behave import given + +@given("step that passes") +def given_step_that_passes(_): + pass + +@given("step which adds a dynamic description to the scenario") +def given_step_which_adds_a_dynamic_description_to_the_scenario(_): + allure.dynamic.description( + "This scenario has a description specified by a step definition" + ) \ No newline at end of file diff --git a/allure-behave/features/hook.feature b/allure-behave/examples/hook.feature similarity index 100% rename from allure-behave/features/hook.feature rename to allure-behave/examples/hook.feature diff --git a/allure-behave/features/label.feature b/allure-behave/examples/label.feature similarity index 100% rename from allure-behave/features/label.feature rename to allure-behave/examples/label.feature diff --git a/allure-behave/features/link.feature b/allure-behave/examples/link.feature similarity index 100% rename from allure-behave/features/link.feature rename to allure-behave/examples/link.feature diff --git a/allure-behave/features/no_skipped.feature b/allure-behave/examples/no_skipped.feature similarity index 100% rename from allure-behave/features/no_skipped.feature rename to allure-behave/examples/no_skipped.feature diff --git a/allure-behave/features/scenario.feature b/allure-behave/examples/scenario.feature similarity index 100% rename from allure-behave/features/scenario.feature rename to allure-behave/examples/scenario.feature diff --git a/allure-behave/features/scenario_outline.feature b/allure-behave/examples/scenario_outline.feature similarity index 100% rename from allure-behave/features/scenario_outline.feature rename to allure-behave/examples/scenario_outline.feature diff --git a/allure-behave/features/severity.feature b/allure-behave/examples/severity.feature similarity index 100% rename from allure-behave/features/severity.feature rename to allure-behave/examples/severity.feature diff --git a/allure-behave/features/step.feature b/allure-behave/examples/step.feature similarity index 100% rename from allure-behave/features/step.feature rename to allure-behave/examples/step.feature diff --git a/allure-behave/features/steps/behave_steps.py b/allure-behave/examples/steps/behave_steps.py similarity index 100% rename from allure-behave/features/steps/behave_steps.py rename to allure-behave/examples/steps/behave_steps.py diff --git a/allure-behave/features/steps/dummy_steps.py b/allure-behave/examples/steps/dummy_steps.py similarity index 97% rename from allure-behave/features/steps/dummy_steps.py rename to allure-behave/examples/steps/dummy_steps.py index e63705ad..de48c0eb 100644 --- a/allure-behave/features/steps/dummy_steps.py +++ b/allure-behave/examples/steps/dummy_steps.py @@ -3,6 +3,7 @@ @given('passed step') +@given("step that passes") @given('{what} passed step') @given('passed step {where}') @given('{what} passed step {where}') diff --git a/allure-behave/features/steps/report_steps.py b/allure-behave/examples/steps/report_steps.py similarity index 100% rename from allure-behave/features/steps/report_steps.py rename to allure-behave/examples/steps/report_steps.py diff --git a/allure-behave/features/tag.feature b/allure-behave/examples/tag.feature similarity index 100% rename from allure-behave/features/tag.feature rename to allure-behave/examples/tag.feature diff --git a/allure-behave/features/test_plan.feature b/allure-behave/examples/test_plan.feature similarity index 100% rename from allure-behave/features/test_plan.feature rename to allure-behave/examples/test_plan.feature diff --git a/allure-behave/features/unicode.feature b/allure-behave/examples/unicode.feature similarity index 100% rename from allure-behave/features/unicode.feature rename to allure-behave/examples/unicode.feature diff --git a/allure-behave/features/background.feature b/allure-behave/features/background.feature deleted file mode 100644 index 53081104..00000000 --- a/allure-behave/features/background.feature +++ /dev/null @@ -1,43 +0,0 @@ -Feature: Background - - Scenario Outline: Background status - Given feature definition - """ - Feature: Background - - Background: Scenario background with step - Given step in background - And another passed step in background - - Scenario: Scenario with background contains step - Given passed step - And one more passed step - - Scenario: Another scenario with background contains step - Given passed step - """ - - When I run behave with allure formatter - - Then allure report has a scenario with name "Scenario with background contains step" - And this scenario has "" status - And this scenario contains step "Given step in background" - And this step has "" status - And this scenario contains step "And another passed step in background" - And this step has "" status - And this scenario contains step "Given passed step" - And this step has "" status - And this scenario contains step "And one more passed step" - And this step has "" status - - Then allure report has a scenario with name "Another scenario with background contains step" - And this scenario has "" status - And this scenario contains step "Given passed step" - And this step has "" status - - Examples: statuses - | step type | status | other status | - | passed | passed | passed | - | failed | failed | skipped | - | broken | broken | skipped | - | undefined | broken | skipped | diff --git a/allure-behave/features/description.feature b/allure-behave/features/description.feature deleted file mode 100644 index 3a8633b6..00000000 --- a/allure-behave/features/description.feature +++ /dev/null @@ -1,36 +0,0 @@ -Feature: Description - - Scenario: Use description - Given feature definition - """ - Feature: Description - - Scenario: Scenario with description - Scenario description. - Yep, multi-line description! - - Given simple passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with description" - - Scenario: Dynamic description - Given feature definition - """ - Feature: Step status - - Scenario: Scenario with passed step - Given simple passed step - """ - And hooks implementation - """ - import allure - import allure_commons - - @allure_commons.fixture - def before_scenario(context, scenario): - allure.dynamic.description("Test description") - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with passed step" - And scenario has description "Test description" \ No newline at end of file diff --git a/allure-pytest-bdd/examples/scenario-outline/outline.feature b/allure-pytest-bdd/examples/scenario-outline/outline.feature new file mode 100644 index 00000000..90291d08 --- /dev/null +++ b/allure-pytest-bdd/examples/scenario-outline/outline.feature @@ -0,0 +1,10 @@ +Feature: Allure report for scenario outline + Scenario Outline: Two examples with two parameters each + Given first step for value + When something is done with the value + Then check postconditions using and + + Examples: + | first | second | + | Alpha | 1 | + | Bravo | 2 | diff --git a/allure-pytest-bdd/examples/scenario-outline/scenario_outline_test.py b/allure-pytest-bdd/examples/scenario-outline/scenario_outline_test.py new file mode 100644 index 00000000..b2fda514 --- /dev/null +++ b/allure-pytest-bdd/examples/scenario-outline/scenario_outline_test.py @@ -0,0 +1,22 @@ +from pytest_bdd import scenario, given, when, then +from pytest_bdd import parsers + + +@scenario("outline.feature", "Two examples with two parameters each") +def test_scenario_outline(): + pass + + +@given(parsers.parse("first step for {first} value")) +def given_first_step_for_first_value(first): + pass + + +@when(parsers.parse("something is done with the value {second}")) +def when_something_is_done_with_the_value_second(second): + pass + + +@then(parsers.parse("check postconditions using {first} and {second}")) +def then_check_postconditions_using_first_and_second(first, second): + pass diff --git a/allure-pytest-bdd/examples/simple-scenario/scenario.feature b/allure-pytest-bdd/examples/simple-scenario/scenario.feature new file mode 100644 index 00000000..b06372c4 --- /dev/null +++ b/allure-pytest-bdd/examples/simple-scenario/scenario.feature @@ -0,0 +1,5 @@ +Feature: Basic allure-pytest-bdd usage + Scenario: Simple passed example + Given the preconditions are satisfied + When the action is invoked + Then the postconditions are held diff --git a/allure-pytest-bdd/examples/simple-scenario/scenario_test.py b/allure-pytest-bdd/examples/simple-scenario/scenario_test.py new file mode 100644 index 00000000..ab66b592 --- /dev/null +++ b/allure-pytest-bdd/examples/simple-scenario/scenario_test.py @@ -0,0 +1,21 @@ +from pytest_bdd import scenario, given, when, then + + +@scenario("scenario.feature", "Simple passed example") +def test_scenario_passes(): + pass + + +@given("the preconditions are satisfied") +def given_the_preconditions_are_satisfied(): + pass + + +@when("the action is invoked") +def when_the_action_is_invoked(): + pass + + +@then("the postconditions are held") +def then_the_postconditions_are_held(): + pass diff --git a/allure-pytest-bdd/features/background.feature b/allure-pytest-bdd/features/background.feature deleted file mode 100644 index 760a0d75..00000000 --- a/allure-pytest-bdd/features/background.feature +++ /dev/null @@ -1 +0,0 @@ -# ToDo ... \ No newline at end of file diff --git a/allure-pytest-bdd/features/outline.feature b/allure-pytest-bdd/features/outline.feature deleted file mode 100644 index 5058739d..00000000 --- a/allure-pytest-bdd/features/outline.feature +++ /dev/null @@ -1,51 +0,0 @@ -Feature: Scenario outline - Scenario: Scenario outline - Given example.feature with content: - """ - Feature: Scenario outline - Scenario Outline: Outline example - Given step - When do nothing - Then step with param - - Examples: - | first | second | - | Alpha | 1 | - | Bravo | 2 | - """ - And example_test.py with content: - """ - from pytest_bdd import scenario - from pytest_bdd import given, then, when, parsers - - @given(parsers.parse("{first} step")) - def given_step(first): - pass - - @when("do nothing") - def nope_step(): - pass - - @then(parsers.parse("step with {second} param")) - def then_step(second): - pass - - @scenario("example.feature", "Outline example") - def test_scenario_outline_example(): - pass - """ - When run pytest-bdd with allure - - Then allure report has result for "Outline example" scenario - Then this scenario has parameter "first" with value "Alpha" - Then this scenario has parameter "second" with value "1" - Then this scenario contains "Given Alpha step" step - Then this scenario contains "Then step with 1 param" step - - Then allure report has result for "Outline example" scenario - Then this scenario has parameter "first" with value "Bravo" - Then this scenario has parameter "second" with value "2" - Then this scenario contains "Given Bravo step" step - Then this scenario contains "Then step with 2 param" step - - diff --git a/allure-pytest-bdd/features/scenario.feature b/allure-pytest-bdd/features/scenario.feature deleted file mode 100644 index c9f402ae..00000000 --- a/allure-pytest-bdd/features/scenario.feature +++ /dev/null @@ -1,32 +0,0 @@ -Feature: Scenario - Scenario: Simple passed scenario - Given example.feature with content: - """ - Feature: Scenario - Scenario: Simple passed example - Given passed step - When passed step - Then passed step - """ - And example_test.py with content: - """ - from pytest_bdd import scenario - from pytest_bdd import given, then, when - - @given("passed step") - def given_passed_step(): - pass - - @when("passed step") - @then("passed step") - def passed_step(): - pass - - @scenario("example.feature", "Simple passed example") - def test_scenario_example(): - pass - """ - When run pytest-bdd with allure - Then allure report has result for "Simple passed example" scenario - Then this scenario has passed status - Then this scenario has a history id diff --git a/allure-pytest-bdd/setup.py b/allure-pytest-bdd/setup.py index bf166279..a97275d9 100644 --- a/allure-pytest-bdd/setup.py +++ b/allure-pytest-bdd/setup.py @@ -34,6 +34,7 @@ def prepare_version(): configuration = {"root": "..", "relative_to": __file__} version = get_version(**configuration) install_requires.append(f"allure-python-commons=={version}") + install_requires.append(f"allure-pytest=={version}") return configuration diff --git a/allure-pytest-bdd/test/conftest.py b/allure-pytest-bdd/test/conftest.py deleted file mode 100644 index 6508c395..00000000 --- a/allure-pytest-bdd/test/conftest.py +++ /dev/null @@ -1,63 +0,0 @@ -import pytest -import mock -from contextlib import contextmanager -import allure_commons -from allure_commons_test.report import AllureReport -from allure_commons.logger import AllureFileLogger -from .steps import * # noqa F401 F403 -from pytest_bdd import given, when, parsers - -pytest_plugins = "pytester" - - -@contextmanager -def fake_logger(path, logger): - blocked_plugins = [] - for name, plugin in allure_commons.plugin_manager.list_name_plugin(): - allure_commons.plugin_manager.unregister(plugin=plugin, name=name) - blocked_plugins.append(plugin) - - with mock.patch(path) as ReporterMock: - ReporterMock.return_value = logger - yield - - for plugin in blocked_plugins: - allure_commons.plugin_manager.register(plugin) - - -class AlluredTestdir: - def __init__(self, testdir, request): - self.testdir = testdir - self.request = request - self.allure_report = None - - def run_with_allure(self): - logger = AllureFileLogger(self.testdir.tmpdir.strpath) - with fake_logger("allure_pytest_bdd.plugin.AllureFileLogger", logger): - self.testdir.runpytest("-s", "-v", "--alluredir", self.testdir.tmpdir) - self.allure_report = AllureReport(self.testdir.tmpdir.strpath) - - -@pytest.fixture -def allured_testdir(testdir, request): - return AlluredTestdir(testdir, request) - - -@pytest.fixture -def context(): - return dict() - - -@pytest.fixture -def allure_report(allured_testdir, context): - return allured_testdir.allure_report - - -@given(parsers.re("(?P\\w+)(?P\\.\\w+) with content:(?:\n)(?P[\\S|\\s]*)")) -def feature_definition(name, extension, content, testdir): - testdir.makefile(extension, **dict([(name, content)])) - - -@when("run pytest-bdd with allure") -def run(allured_testdir): - allured_testdir.run_with_allure() diff --git a/allure-pytest-bdd/test/outline_test.py b/allure-pytest-bdd/test/outline_test.py deleted file mode 100644 index d176148a..00000000 --- a/allure-pytest-bdd/test/outline_test.py +++ /dev/null @@ -1,6 +0,0 @@ -from pytest_bdd import scenario - - -@scenario("../features/outline.feature", "Scenario outline") -def test_scenario_outline(): - pass diff --git a/allure-pytest-bdd/test/scenario_test.py b/allure-pytest-bdd/test/scenario_test.py deleted file mode 100644 index 18362409..00000000 --- a/allure-pytest-bdd/test/scenario_test.py +++ /dev/null @@ -1,6 +0,0 @@ -from pytest_bdd import scenario - - -@scenario("../features/scenario.feature", "Simple passed scenario") -def test_simple_passed_scenario(): - pass diff --git a/allure-pytest-bdd/test/steps.py b/allure-pytest-bdd/test/steps.py deleted file mode 100644 index eac3cc95..00000000 --- a/allure-pytest-bdd/test/steps.py +++ /dev/null @@ -1,57 +0,0 @@ -from pytest_bdd import then -from pytest_bdd import parsers -from functools import partial -from hamcrest import assert_that -from allure_commons_test.report import has_test_case -from allure_commons_test.result import with_status -from allure_commons_test.result import has_step -from allure_commons_test.result import has_parameter -from allure_commons_test.result import has_history_id - - -def match(matcher, *args): - for i, arg in enumerate(args): - if not hasattr(arg, '__call__'): - matcher = partial(matcher, arg) - else: - matcher = partial(matcher, match(arg, *args[i+1:])) - break - return matcher() - - -@then(parsers.re("allure report has result for (?:\")(?P[\\w|\\s|,]*)(?:\") scenario")) -def match_scenario(allure_report, context, scenario_name): - matcher = partial(match, has_test_case, scenario_name) - assert_that(allure_report, matcher()) - context['scenario'] = matcher - - -@then(parsers.parse("this {item:w} has {status:w} status")) -def item_status(allure_report, context, item, status): - context_matcher = context[item] - matcher = partial(context_matcher, with_status, status) - assert_that(allure_report, matcher()) - - -@then(parsers.parse("this {item:w} has a history id")) -def item_history_id(allure_report, context, item): - context_matcher = context[item] - matcher = partial(context_matcher, has_history_id) - assert_that(allure_report, matcher()) - - -@then(parsers.re("this (?P\\w+) " - "has parameter (?:\")(?P[\\w|\\s]*)(?:\") " - "with value (?:\")(?P[\\w|\\s]*)(?:\")")) -def item_parameter(allure_report, context, item, param_name, param_value): - context_matcher = context[item] - matcher = partial(context_matcher, has_parameter, param_name, param_value) - assert_that(allure_report, matcher()) - - -@then(parsers.re("this (?P\\w+) contains (?:\")(?P[\\w|\\s|>|<]+)(?:\") step")) -def step_step(allure_report, context, item, step): - context_matcher = context[item] - matcher = partial(context_matcher, has_step, step) - context["step"] = matcher - assert_that(allure_report, matcher()) diff --git a/allure-pytest/test/acceptance/label/package/regression_test.py b/allure-pytest/test/acceptance/label/package/regression_test.py deleted file mode 100644 index 358fdb5a..00000000 --- a/allure-pytest/test/acceptance/label/package/regression_test.py +++ /dev/null @@ -1,41 +0,0 @@ -import textwrap -from hamcrest import assert_that -from hamcrest import ends_with -from allure_commons_test.report import has_test_case -from allure_commons_test.label import has_package - - -def test_path_with_dots_test(allured_testdir): - path = allured_testdir.testdir.mkpydir("path.with.dots") - - path.join("test_path.py").write( - textwrap.dedent( - """\ - def test_path_with_dots_test_example(): - pass - """) - ) - - allured_testdir.run_with_allure() - - assert_that(allured_testdir.allure_report, - has_test_case("test_path_with_dots_test_example", - has_package(ends_with("path.with.dots.test_path")) - ) - ) - - -def test_with_no_package(allured_testdir): - """ - >>> def test_package_less(request): - ... pass - """ - allured_testdir.parse_docstring_source() - - allured_testdir.testdir.makeini("""[pytest]""") - allured_testdir.run_with_allure(allured_testdir.testdir.tmpdir) - - assert_that(allured_testdir.allure_report, - has_test_case("test_package_less", - has_package("test_with_no_package")) - ) diff --git a/allure-pytest/test/acceptance/link/link_pattern_test.py b/allure-pytest/test/acceptance/link/link_pattern_test.py deleted file mode 100644 index 28d251b9..00000000 --- a/allure-pytest/test/acceptance/link/link_pattern_test.py +++ /dev/null @@ -1,22 +0,0 @@ -from hamcrest import assert_that -from allure_commons_test.report import has_test_case -from allure_commons_test.result import has_link, has_issue_link - - -def test_link_pattern(allured_testdir): - """ ./examples/link/dynamic_link.rst """ - - allured_testdir.parse_docstring_path() - - allured_testdir.run_with_allure("--allure-link-pattern", - "issue:https://github.com/allure-framework/allure-python2/{}", - "--allure-link-pattern", - "docs:https://docs.qameta.io/{}") - - assert_that(allured_testdir.allure_report, - has_test_case("test_all_links_together", - has_issue_link("https://github.com/allure-framework/allure-python2/issues/24"), - has_issue_link("https://github.com/allure-framework/allure-python2/issues/24"), - has_link("https://docs.qameta.io/allure") - ) - ) diff --git a/allure-pytest/test/acceptance/step/step_placeholder_test.py b/allure-pytest/test/acceptance/step/step_placeholder_test.py deleted file mode 100644 index 6c4eda5f..00000000 --- a/allure-pytest/test/acceptance/step/step_placeholder_test.py +++ /dev/null @@ -1,51 +0,0 @@ -""" ./examples/step/step_placeholder.rst """ -import pytest -from hamcrest import assert_that -from allure_commons_test.report import has_test_case -from allure_commons_test.result import has_step -from allure_commons_test.result import has_status_details -from allure_commons_test.result import with_message_contains - - -def test_step_with_args_in_placeholder(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_step_with_args_in_placeholder", - has_step("Step with two args: 'first' and 'second'") - ) - ) - - -def test_step_with_kwargs_in_placeholder(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_step_with_kwargs_in_placeholder", - has_step("Step with two kwargs: '1' and 'second'") - ) - ) - - -def test_class_method_as_step(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_class_method_as_step", - has_step("Class method step with 'first' and 'second'") - ) - ) - - -@pytest.mark.skip() -def test_args_less_than_placeholders(executed_docstring_source): - """ - >>> import allure - - >>> @allure.step("{0} and {1}") - ... def step(arg): - ... pass - - >>> def test_args_less_than_placeholders_example(): - ... step(0) - """ - - assert_that(executed_docstring_source.allure_report, - has_test_case("test_args_less_than_placeholders_example", - has_status_details(with_message_contains("IndexError: tuple index out of range")) - ) - ) diff --git a/allure-pytest/test/conftest.py b/allure-pytest/test/conftest.py deleted file mode 100644 index a5f52417..00000000 --- a/allure-pytest/test/conftest.py +++ /dev/null @@ -1,80 +0,0 @@ -import pytest -from allure_commons_test.report import AllureReport -from doctest import script_from_examples -import mock -import allure_commons -from contextlib import contextmanager -from allure_commons.logger import AllureMemoryLogger - -pytest_plugins = "pytester" - - -def pytest_configure(config): - config.addinivalue_line( - "markers", "real_logger: mark test to run with a real allure logger" - ) - - -@contextmanager -def fake_logger(path, logger): - blocked_plugins = [] - for name, plugin in allure_commons.plugin_manager.list_name_plugin(): - allure_commons.plugin_manager.unregister(plugin=plugin, name=name) - blocked_plugins.append(plugin) - - with mock.patch(path) as ReporterMock: - ReporterMock.return_value = logger - yield - - for plugin in blocked_plugins: - allure_commons.plugin_manager.register(plugin) - - -class AlluredTestdir: - def __init__(self, testdir, request): - self.testdir = testdir - self.request = request - self.allure_report = None - - def parse_docstring_source(self): - docstring = self.request.node.function.__doc__ or self.request.node.module.__doc__ - source = script_from_examples(docstring).replace("#\n", "\n") - self.testdir.makepyfile(source) - - def parse_docstring_path(self): - doc_file = self.request.node.function.__doc__ or self.request.node.module.__doc__ - example_dir = self.request.config.rootdir.join(doc_file.strip()) - with open(example_dir, encoding="utf-8") as f: - content = f.read() - source = script_from_examples(content) - self.testdir.makepyfile(source) - - def run_with_allure(self, *args, **kwargs): - if self.request.node.get_closest_marker("real_logger"): - self.testdir.runpytest("--alluredir", self.testdir.tmpdir, *args, **kwargs) - self.allure_report = AllureReport(self.testdir.tmpdir.strpath) - else: - self.allure_report = AllureMemoryLogger() - with fake_logger("allure_pytest.plugin.AllureFileLogger", self.allure_report): - self.testdir.runpytest("--alluredir", self.testdir.tmpdir, *args, **kwargs) - - return self.allure_report - - -@pytest.fixture -def allured_testdir(testdir, request): - return AlluredTestdir(testdir, request) - - -@pytest.fixture -def executed_docstring_source(allured_testdir): - allured_testdir.parse_docstring_source() - allured_testdir.run_with_allure() - return allured_testdir - - -@pytest.fixture -def executed_docstring_path(allured_testdir): - allured_testdir.parse_docstring_path() - allured_testdir.run_with_allure() - return allured_testdir diff --git a/allure-pytest/test/integration/allure_ee/select_test_from_testplan_test.py b/allure-pytest/test/integration/allure_ee/select_test_from_testplan_test.py deleted file mode 100644 index f7dd303c..00000000 --- a/allure-pytest/test/integration/allure_ee/select_test_from_testplan_test.py +++ /dev/null @@ -1,102 +0,0 @@ -import pytest -import json -import os -from hamcrest import assert_that, contains_inanyorder, ends_with - - -@pytest.mark.parametrize( - ["planned_tests", "expected_tests"], - [ - # by ids only - ( - [{"id": 1}, {"id": 2}], - ["test_number_one", "test_number_two"] - ), - - # by ids for multiply decorated test - ( - [{"id": 1}, {"id": 3}, {"id": 4}], - ["test_number_one", "test_number_three"] - ), - - # by wrong id - ( - [{"id": 1234}], - [] - ), - - # by selectors only - ( - [{"selector": "test_number_one"}, {"selector": "test_number_three"}], - ["test_number_one", "test_number_three"] - ), - - # by selector for not decorated with id test - ( - [{"selector": "test_without_number"}], - ["test_without_number"] - ), - - # by id and selector for same test - ( - [{"id": 2, "selector": "test_number_two"}], - ["test_number_two"] - ), - - # by wrong selector - ( - [{"selector": "test_without_never"}], - [] - ), - - # without plan - ( - None, - ["test_number_one", "test_number_two", "test_number_three", "test_without_number"] - ), - ] -) -def test_select_by_testcase_id_test(planned_tests, expected_tests, allured_testdir, request): - """ - >>> import allure - - >>> @allure.id("1") - ... def test_number_one(): - ... pass - - >>> @allure.id("2") - ... def test_number_two(): - ... pass - - >>> @allure.id("3") - ... @allure.id("4") - ... def test_number_three(): - ... pass - - >>> def test_without_number(): - ... pass - """ - - root_dir = request.config.rootdir.strpath - test_dir = allured_testdir.testdir.tmpdir.strpath.replace(root_dir, "") - base_path = test_dir.strip(os.sep).replace(os.sep, ".") - full_name_base_template = f"{base_path}.test_select_by_testcase_id_test" - - if planned_tests: - for item in planned_tests: - if "selector" in item: - selector = item['selector'] - item["selector"] = f"{full_name_base_template}#{selector}" - - testplan = {"tests": planned_tests} - py_path = allured_testdir.testdir.makefile(".json", json.dumps(testplan)) - os.environ["ALLURE_TESTPLAN_PATH"] = py_path.strpath - else: - os.environ.pop("ALLURE_TESTPLAN_PATH", None) - - allured_testdir.parse_docstring_source() - allured_testdir.run_with_allure() - - executed_full_names = [test_case["fullName"] for test_case in allured_testdir.allure_report.test_cases] - - assert_that(executed_full_names, contains_inanyorder(*[ends_with(name) for name in expected_tests])) diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..e5e8a84e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,8 @@ +[tool.pytest.ini_options] +testpaths = [ + "tests" +] +addopts = [ + "-p", "no:allure_pytest", + "-p", "no:allure_pytest_bdd" +] \ No newline at end of file diff --git a/allure-pytest-bdd/test/__init__.py b/tests/__init__.py similarity index 100% rename from allure-pytest-bdd/test/__init__.py rename to tests/__init__.py diff --git a/tests/allure_behave/acceptance/background/background.feature b/tests/allure_behave/acceptance/background/background.feature new file mode 100644 index 00000000..7f6ae0e9 --- /dev/null +++ b/tests/allure_behave/acceptance/background/background.feature @@ -0,0 +1,11 @@ +Feature: Allure-behave compatibility with feature backgrounds + Background: A background with {bg_step_status} step + Given the first background step that is {bg_step_status} + And the second background step with no failures + + Scenario: Scenario with background containing {bg_step_status} step + Given the first step with no failures + And the second step with no failures + + Scenario: Another scenario with background containing {bg_step_status} step + Given the step with no failures diff --git a/tests/allure_behave/acceptance/background/background_steps.py b/tests/allure_behave/acceptance/background/background_steps.py new file mode 100644 index 00000000..db066e80 --- /dev/null +++ b/tests/allure_behave/acceptance/background/background_steps.py @@ -0,0 +1,23 @@ +from behave import given + +@given("the first background step that is passed") +def the_first_background_step_that_is_passed(*args, **kwargs): + pass + + +@given("the first background step that is failed") +def the_first_background_step_that_is_failed(*args, **kwargs): + assert False, "Failed assertion message" + + +@given("the first background step that is broken") +def the_first_background_step_that_is_broken(*args, **kwargs): + raise ValueError("Something is broken") + + +@given("the second background step with no failures") +@given("the first step with no failures") +@given("the second step with no failures") +@given("the step with no failures") +def the_step_with_no_failures(*args, **kwargs): + pass diff --git a/tests/allure_behave/acceptance/background/background_test.py b/tests/allure_behave/acceptance/background/background_test.py new file mode 100644 index 00000000..68bf2324 --- /dev/null +++ b/tests/allure_behave/acceptance/background/background_test.py @@ -0,0 +1,70 @@ +import pytest +from behave.parser import Parser +from hamcrest import assert_that, all_of +from tests.allure_behave.conftest import AllureBehaveRunner +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status +from allure_commons_test.result import has_step + + +@pytest.mark.parametrize( + ["step_outcome", "status", "remained_steps_status"], + [ + pytest.param("passed", "passed", "passed", id="passed"), + pytest.param("failed", "failed", "skipped", id="failed"), + pytest.param("broken", "broken", "skipped", id="broken"), + pytest.param("undefined", "broken", "skipped", id="undefined"), + ] +) +def test_background( + allure_behave_runner: AllureBehaveRunner, + step_outcome: str, + status: str, + remained_steps_status: str +): + allure_behave_runner.run_feature_of_current_test( + bg_step_status=step_outcome + ) + assert_that( + allure_behave_runner.allure_results, + has_test_case( + f"Scenario with background containing {step_outcome} step", + all_of( + with_status(status), + has_step( + f"Given the first background step that is {step_outcome}", + with_status(status) + ), + has_step( + "And the second background step with no failures", + with_status(remained_steps_status) + ), + has_step( + "Given the first step with no failures", + with_status(remained_steps_status) + ), + has_step( + "And the second step with no failures", + with_status(remained_steps_status) + ) + ) + ), + has_test_case( + f"Another scenario with background containing {step_outcome} step", + all_of( + with_status(status), + has_step( + f"Given the first background step that is {step_outcome}", + with_status(status) + ), + has_step( + "And the second background step with no failures", + with_status(remained_steps_status) + ), + has_step( + "Given the step with no failures", + with_status(remained_steps_status) + ) + ) + ) + ) \ No newline at end of file diff --git a/tests/allure_behave/acceptance/description/description_test.py b/tests/allure_behave/acceptance/description/description_test.py new file mode 100644 index 00000000..7c63c4d0 --- /dev/null +++ b/tests/allure_behave/acceptance/description/description_test.py @@ -0,0 +1,52 @@ +""" ./allure-behave/examples/description """ + +from tests.allure_behave.conftest import AllureBehaveRunner +from hamcrest import assert_that, all_of +from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_description + + +def test_descriptions_from_feature_file(executed_docstring_path: AllureBehaveRunner): + assert_that( + executed_docstring_path.allure_results, + has_test_case( + "Description from a .feature file", + has_description("This scenario has a description.\nThis description spans across multiple lines.") + ) + ) + + +def test_descriptions_from_step(executed_docstring_path: AllureBehaveRunner): + assert_that( + executed_docstring_path.allure_results, + has_test_case( + "Description from a step definition function", + has_description( + "This scenario has a description specified by a step definition" + ) + ) + ) + + +def test_descriptions_before_scenario(executed_docstring_path: AllureBehaveRunner): + assert_that( + executed_docstring_path.allure_results, + has_test_case( + "Description from before_scenario hook", + has_description( + "This scenario has a description specified in before_scenario hook" + ) + ) + ) + + +def test_descriptions_after_scenario(executed_docstring_path: AllureBehaveRunner): + assert_that( + executed_docstring_path.allure_results, + has_test_case( + "Description from after_scenario hook", + has_description( + "This scenario has a description specified in after_scenario hook" + ) + ) + ) diff --git a/tests/allure_behave/conftest.py b/tests/allure_behave/conftest.py new file mode 100644 index 00000000..238e184d --- /dev/null +++ b/tests/allure_behave/conftest.py @@ -0,0 +1,93 @@ +import shutil +from pytest import FixtureRequest, fixture, Pytester +from tests.conftest import AllureIntegrationRunner +from tests.conftest import get_path_from_docstring +from behave.runner import Runner +import behave.step_registry +from behave.step_registry import StepRegistry + + +def __fix_behave_multirun(): + # behave doesn't play nicely with consecutive programmatic runs, so we + # force it to do a proper reset here + original_run_model = Runner.run_model + def __fixed_run_model(self, *args, **kwargs): + # Originally, the runner caches the instance of step_registry on a + # module level and reuse that same registry on each run, resulting in + # disparity between step registration and step matching. + # Here we force the runner to use the newest instance of registry. + self.step_registry = behave.step_registry.registry + return original_run_model(self, *args, **kwargs) + Runner.run_model = __fixed_run_model + + original_add_step_definition = StepRegistry.add_step_definition + def __fixed_add_step_definition(self, *args, **kwargs): + # The same happens with step registration mechanism embedded into + # bdd declarators (given, then, etc). + # Here we redirect add_step_definition method to the newest instance of + # registry. + return original_add_step_definition( + behave.step_registry.registry, + *args, + **kwargs + ) + StepRegistry.add_step_definition = __fixed_add_step_definition + + +class AllureBehaveRunner: + def __init__(self, pytester: Pytester, request: FixtureRequest): + self.pytester = pytester + self.request = request + self.runner = AllureIntegrationRunner("behave") + self.exit_code = None + self.allure_results = None + + def run_allure_behave(self, *args: str): + result = self.runner.run_allure_integration(( + "--no-snippets", + "-f", "allure_behave.formatter:AllureFormatter", + "-o", "allure-results", + *args + )) + self.exit_code = result["return-value"] + self.allure_results = result["allure-results"] + + def run_feature_by_docstring_path(self): + self.run_allure_behave( + get_path_from_docstring(self.request) + ) + + def run_feature_of_current_test(self, **fmt_kwargs): + test_folder = self.request.node.path.parent + self.__generate_test_features(test_folder, fmt_kwargs) + self.__copy_test_feature_steps(test_folder) + self.run_allure_behave(self.pytester.path) + + def __generate_test_features(self, test_folder, fmt_kwargs): + for feature_path in test_folder.glob("*.feature"): + dst_path = self.pytester.path.joinpath(feature_path.name) + with open(feature_path, "r", encoding="utf-8") as src: + with open(dst_path, "w+", encoding="utf-8") as dst: + dst.writelines( + line.format_map(fmt_kwargs) for line in src.readlines() + ) + + def __copy_test_feature_steps(self, test_folder): + tmp_steps_folder = self.pytester.path.joinpath("steps") + tmp_steps_folder.mkdir(exist_ok=True) + for step_path in test_folder.glob("*_steps.py"): + if not step_path.name.startswith("test_"): + dst_path = tmp_steps_folder.joinpath(step_path.name) + shutil.copyfile(step_path, dst_path) + +@fixture +def allure_behave_runner(pytester: Pytester, request: FixtureRequest): + return AllureBehaveRunner(pytester, request) + + +@fixture +def executed_docstring_path(allure_behave_runner): + allure_behave_runner.run_feature_by_docstring_path() + return allure_behave_runner + +__fix_behave_multirun() \ No newline at end of file diff --git a/allure-pytest/test/__init__.py b/tests/allure_pytest/__init__.py similarity index 100% rename from allure-pytest/test/__init__.py rename to tests/allure_pytest/__init__.py diff --git a/allure-pytest/test/acceptance/__init__.py b/tests/allure_pytest/acceptance/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/__init__.py rename to tests/allure_pytest/acceptance/__init__.py diff --git a/allure-pytest/test/acceptance/attachment/__init__.py b/tests/allure_pytest/acceptance/attachment/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/attachment/__init__.py rename to tests/allure_pytest/acceptance/attachment/__init__.py diff --git a/allure-pytest/test/acceptance/attachment/attachment_class_test.py b/tests/allure_pytest/acceptance/attachment/attachment_class_test.py similarity index 88% rename from allure-pytest/test/acceptance/attachment/attachment_class_test.py rename to tests/allure_pytest/acceptance/attachment/attachment_class_test.py index 6f229e27..a5c70521 100644 --- a/allure-pytest/test/acceptance/attachment/attachment_class_test.py +++ b/tests/allure_pytest/acceptance/attachment/attachment_class_test.py @@ -3,7 +3,7 @@ from allure_commons_test.result import has_attachment -def test_class_method_attachment(executed_docstring_source): +def test_class_method_attachment(executed_docstring_source, request): """ >>> import allure diff --git a/allure-pytest/test/acceptance/attachment/attachment_fixture_test.py b/tests/allure_pytest/acceptance/attachment/attachment_fixture_test.py similarity index 95% rename from allure-pytest/test/acceptance/attachment/attachment_fixture_test.py rename to tests/allure_pytest/acceptance/attachment/attachment_fixture_test.py index caaf4f67..734b1b5f 100644 --- a/allure-pytest/test/acceptance/attachment/attachment_fixture_test.py +++ b/tests/allure_pytest/acceptance/attachment/attachment_fixture_test.py @@ -1,4 +1,4 @@ -""" ./examples/attachment/attachment_fixture.rst """ +""" ./allure-pytest/examples/attachment/attachment_fixture.rst """ from hamcrest import assert_that from allure_commons_test.report import has_test_case diff --git a/allure-pytest/test/acceptance/attachment/attachment_hook_test.py b/tests/allure_pytest/acceptance/attachment/attachment_hook_test.py similarity index 100% rename from allure-pytest/test/acceptance/attachment/attachment_hook_test.py rename to tests/allure_pytest/acceptance/attachment/attachment_hook_test.py diff --git a/allure-pytest/test/acceptance/attachment/attachment_parametrized_test.py b/tests/allure_pytest/acceptance/attachment/attachment_parametrized_test.py similarity index 100% rename from allure-pytest/test/acceptance/attachment/attachment_parametrized_test.py rename to tests/allure_pytest/acceptance/attachment/attachment_parametrized_test.py diff --git a/allure-pytest/test/acceptance/attachment/attachment_step_test.py b/tests/allure_pytest/acceptance/attachment/attachment_step_test.py similarity index 96% rename from allure-pytest/test/acceptance/attachment/attachment_step_test.py rename to tests/allure_pytest/acceptance/attachment/attachment_step_test.py index bdb14d44..da544978 100644 --- a/allure-pytest/test/acceptance/attachment/attachment_step_test.py +++ b/tests/allure_pytest/acceptance/attachment/attachment_step_test.py @@ -1,4 +1,4 @@ -""" ./examples/attachment/attachment_step.rst """ +""" ./allure-pytest/examples/attachment/attachment_step.rst """ from hamcrest import assert_that from allure_commons_test.report import has_test_case diff --git a/allure-pytest/test/acceptance/attachment/attachment_test.py b/tests/allure_pytest/acceptance/attachment/attachment_test.py similarity index 94% rename from allure-pytest/test/acceptance/attachment/attachment_test.py rename to tests/allure_pytest/acceptance/attachment/attachment_test.py index 46a2df94..8fd3c596 100644 --- a/allure-pytest/test/acceptance/attachment/attachment_test.py +++ b/tests/allure_pytest/acceptance/attachment/attachment_test.py @@ -1,4 +1,4 @@ -""" ./examples/attachment/attachment.rst """ +""" ./allure-pytest/examples/attachment/attachment.rst """ from hamcrest import assert_that from allure_commons_test.report import has_test_case diff --git a/allure-pytest/test/acceptance/capture/__init__.py b/tests/allure_pytest/acceptance/capture/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/capture/__init__.py rename to tests/allure_pytest/acceptance/capture/__init__.py diff --git a/allure-pytest/test/acceptance/capture/capture_attach_test.py b/tests/allure_pytest/acceptance/capture/capture_attach_test.py similarity index 100% rename from allure-pytest/test/acceptance/capture/capture_attach_test.py rename to tests/allure_pytest/acceptance/capture/capture_attach_test.py diff --git a/allure-pytest/test/acceptance/description/__init__.py b/tests/allure_pytest/acceptance/description/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/description/__init__.py rename to tests/allure_pytest/acceptance/description/__init__.py diff --git a/allure-pytest/test/acceptance/description/description_test.py b/tests/allure_pytest/acceptance/description/description_test.py similarity index 95% rename from allure-pytest/test/acceptance/description/description_test.py rename to tests/allure_pytest/acceptance/description/description_test.py index bec2fbac..dd28fc1e 100644 --- a/allure-pytest/test/acceptance/description/description_test.py +++ b/tests/allure_pytest/acceptance/description/description_test.py @@ -1,4 +1,4 @@ -""" ./examples/description/description.rst """ +""" ./allure-pytest/examples/description/description.rst """ from hamcrest import assert_that, contains_string from allure_commons_test.report import has_test_case diff --git a/allure-pytest/test/acceptance/description/dynamic_description_test.py b/tests/allure_pytest/acceptance/description/dynamic_description_test.py similarity index 92% rename from allure-pytest/test/acceptance/description/dynamic_description_test.py rename to tests/allure_pytest/acceptance/description/dynamic_description_test.py index 66091476..f98fe5f9 100644 --- a/allure-pytest/test/acceptance/description/dynamic_description_test.py +++ b/tests/allure_pytest/acceptance/description/dynamic_description_test.py @@ -1,4 +1,4 @@ -"""./examples/description/dynamic_description.rst""" +"""./allure-pytest/examples/description/dynamic_description.rst""" from hamcrest import assert_that, contains_string from allure_commons_test.report import has_test_case diff --git a/allure-pytest/test/acceptance/display_name/__init__.py b/tests/allure_pytest/acceptance/display_name/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/display_name/__init__.py rename to tests/allure_pytest/acceptance/display_name/__init__.py diff --git a/allure-pytest/test/acceptance/display_name/display_name_test.py b/tests/allure_pytest/acceptance/display_name/display_name_test.py similarity index 98% rename from allure-pytest/test/acceptance/display_name/display_name_test.py rename to tests/allure_pytest/acceptance/display_name/display_name_test.py index a3021970..d017cc6d 100644 --- a/allure-pytest/test/acceptance/display_name/display_name_test.py +++ b/tests/allure_pytest/acceptance/display_name/display_name_test.py @@ -1,4 +1,4 @@ -""" ./examples/display_name/display_name.rst""" +""" ./allure-pytest/examples/display_name/display_name.rst""" from hamcrest import assert_that from allure_commons_test.report import has_test_case diff --git a/allure-pytest/test/acceptance/display_name/dynamic_display_name_test.py b/tests/allure_pytest/acceptance/display_name/dynamic_display_name_test.py similarity index 85% rename from allure-pytest/test/acceptance/display_name/dynamic_display_name_test.py rename to tests/allure_pytest/acceptance/display_name/dynamic_display_name_test.py index cfd34d9d..af243cb2 100644 --- a/allure-pytest/test/acceptance/display_name/dynamic_display_name_test.py +++ b/tests/allure_pytest/acceptance/display_name/dynamic_display_name_test.py @@ -1,11 +1,11 @@ +""" ./allure-pytest/examples/display_name/dynamic_display_name.rst """ + from hamcrest import assert_that from allure_commons_test.report import has_test_case from allure_commons_test.result import has_title def test_dynamic_display_name(executed_docstring_path): - """ ./examples/display_name/dynamic_display_name.rst """ - assert_that(executed_docstring_path.allure_report, has_test_case("test_dynamic_display_name", has_title("It is renamed test") diff --git a/allure-pytest/test/acceptance/duration/__init__.py b/tests/allure_pytest/acceptance/duration/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/duration/__init__.py rename to tests/allure_pytest/acceptance/duration/__init__.py diff --git a/allure-pytest/test/acceptance/duration/duration_time_test.py b/tests/allure_pytest/acceptance/duration/duration_time_test.py similarity index 100% rename from allure-pytest/test/acceptance/duration/duration_time_test.py rename to tests/allure_pytest/acceptance/duration/duration_time_test.py diff --git a/allure-pytest/test/acceptance/fixture/__init__.py b/tests/allure_pytest/acceptance/fixture/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/fixture/__init__.py rename to tests/allure_pytest/acceptance/fixture/__init__.py diff --git a/allure-pytest/test/acceptance/fixture/fixture_finalized_test.py b/tests/allure_pytest/acceptance/fixture/fixture_finalized_test.py similarity index 100% rename from allure-pytest/test/acceptance/fixture/fixture_finalized_test.py rename to tests/allure_pytest/acceptance/fixture/fixture_finalized_test.py diff --git a/allure-pytest/test/acceptance/fixture/fixture_test.py b/tests/allure_pytest/acceptance/fixture/fixture_test.py similarity index 100% rename from allure-pytest/test/acceptance/fixture/fixture_test.py rename to tests/allure_pytest/acceptance/fixture/fixture_test.py diff --git a/allure-pytest/test/acceptance/fixture/function_scope/__init__.py b/tests/allure_pytest/acceptance/fixture/function_scope/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/fixture/function_scope/__init__.py rename to tests/allure_pytest/acceptance/fixture/function_scope/__init__.py diff --git a/allure-pytest/test/acceptance/fixture/parametrized_fixture_test.py b/tests/allure_pytest/acceptance/fixture/parametrized_fixture_test.py similarity index 100% rename from allure-pytest/test/acceptance/fixture/parametrized_fixture_test.py rename to tests/allure_pytest/acceptance/fixture/parametrized_fixture_test.py diff --git a/allure-pytest/test/acceptance/fixture/yield_fixture_test.py b/tests/allure_pytest/acceptance/fixture/yield_fixture_test.py similarity index 100% rename from allure-pytest/test/acceptance/fixture/yield_fixture_test.py rename to tests/allure_pytest/acceptance/fixture/yield_fixture_test.py diff --git a/allure-pytest/test/acceptance/history_id/__init__.py b/tests/allure_pytest/acceptance/history_id/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/history_id/__init__.py rename to tests/allure_pytest/acceptance/history_id/__init__.py diff --git a/allure-pytest/test/acceptance/history_id/history_id_test.py b/tests/allure_pytest/acceptance/history_id/history_id_test.py similarity index 100% rename from allure-pytest/test/acceptance/history_id/history_id_test.py rename to tests/allure_pytest/acceptance/history_id/history_id_test.py diff --git a/allure-pytest/test/acceptance/label/__init__.py b/tests/allure_pytest/acceptance/label/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/label/__init__.py rename to tests/allure_pytest/acceptance/label/__init__.py diff --git a/allure-pytest/test/acceptance/label/bdd/__init__.py b/tests/allure_pytest/acceptance/label/bdd/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/label/bdd/__init__.py rename to tests/allure_pytest/acceptance/label/bdd/__init__.py diff --git a/allure-pytest/test/acceptance/label/bdd/bdd_label_test.py b/tests/allure_pytest/acceptance/label/bdd/bdd_label_test.py similarity index 95% rename from allure-pytest/test/acceptance/label/bdd/bdd_label_test.py rename to tests/allure_pytest/acceptance/label/bdd/bdd_label_test.py index 28d72275..df1c1430 100644 --- a/allure-pytest/test/acceptance/label/bdd/bdd_label_test.py +++ b/tests/allure_pytest/acceptance/label/bdd/bdd_label_test.py @@ -1,4 +1,4 @@ -""" ./examples/label/bdd/bdd_label.rst """ +""" ./allure-pytest/examples/label/bdd/bdd_label.rst """ from hamcrest import assert_that from allure_commons_test.report import has_test_case diff --git a/allure-pytest/test/acceptance/label/bdd/dynamic_bdd_label_test.py b/tests/allure_pytest/acceptance/label/bdd/dynamic_bdd_label_test.py similarity index 96% rename from allure-pytest/test/acceptance/label/bdd/dynamic_bdd_label_test.py rename to tests/allure_pytest/acceptance/label/bdd/dynamic_bdd_label_test.py index cb80f65a..e3a9f51c 100644 --- a/allure-pytest/test/acceptance/label/bdd/dynamic_bdd_label_test.py +++ b/tests/allure_pytest/acceptance/label/bdd/dynamic_bdd_label_test.py @@ -1,4 +1,4 @@ -""" ./examples/label/bdd/dynamic_bdd_label.rst """ +""" ./allure-pytest/examples/label/bdd/dynamic_bdd_label.rst """ import pytest from hamcrest import assert_that diff --git a/allure-pytest/test/acceptance/label/bdd/select_bdd_test.py b/tests/allure_pytest/acceptance/label/bdd/select_bdd_test.py similarity index 95% rename from allure-pytest/test/acceptance/label/bdd/select_bdd_test.py rename to tests/allure_pytest/acceptance/label/bdd/select_bdd_test.py index fe13c4b2..7faeff29 100644 --- a/allure-pytest/test/acceptance/label/bdd/select_bdd_test.py +++ b/tests/allure_pytest/acceptance/label/bdd/select_bdd_test.py @@ -1,4 +1,4 @@ -""" ./examples/label/bdd/select_tests_by_bdd.rst """ +""" ./allure-pytest/examples/label/bdd/select_tests_by_bdd.rst """ import pytest from hamcrest import assert_that, only_contains, any_of, ends_with diff --git a/allure-pytest/test/acceptance/label/custom/__init__.py b/tests/allure_pytest/acceptance/label/custom/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/label/custom/__init__.py rename to tests/allure_pytest/acceptance/label/custom/__init__.py diff --git a/allure-pytest/test/acceptance/label/custom/custom_label_test.py b/tests/allure_pytest/acceptance/label/custom/custom_label_test.py similarity index 88% rename from allure-pytest/test/acceptance/label/custom/custom_label_test.py rename to tests/allure_pytest/acceptance/label/custom/custom_label_test.py index 1de5f0ce..3e943d43 100644 --- a/allure-pytest/test/acceptance/label/custom/custom_label_test.py +++ b/tests/allure_pytest/acceptance/label/custom/custom_label_test.py @@ -1,4 +1,4 @@ -""" ./examples/label/custom/custom_label.rst """ +""" ./allure-pytest/examples/label/custom/custom_label.rst """ from hamcrest import assert_that from allure_commons_test.report import has_test_case diff --git a/allure-pytest/test/acceptance/label/custom/select_custom_label_test.py b/tests/allure_pytest/acceptance/label/custom/select_custom_label_test.py similarity index 95% rename from allure-pytest/test/acceptance/label/custom/select_custom_label_test.py rename to tests/allure_pytest/acceptance/label/custom/select_custom_label_test.py index e92b8fbc..02cbdcc9 100644 --- a/allure-pytest/test/acceptance/label/custom/select_custom_label_test.py +++ b/tests/allure_pytest/acceptance/label/custom/select_custom_label_test.py @@ -1,4 +1,4 @@ -""" ./examples/label/custom/select_tests_by_label.rst """ +""" ./allure-pytest/examples/label/custom/select_tests_by_label.rst """ import pytest from hamcrest import assert_that, ends_with, contains_inanyorder diff --git a/allure-pytest/test/integration/allure_ee/set_testcase_id_test.py b/tests/allure_pytest/acceptance/label/id/set_testcase_id_test.py similarity index 100% rename from allure-pytest/test/integration/allure_ee/set_testcase_id_test.py rename to tests/allure_pytest/acceptance/label/id/set_testcase_id_test.py diff --git a/allure-pytest/test/acceptance/label/manual/__init__.py b/tests/allure_pytest/acceptance/label/manual/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/label/manual/__init__.py rename to tests/allure_pytest/acceptance/label/manual/__init__.py diff --git a/allure-pytest/test/acceptance/label/manual/manual_test.py b/tests/allure_pytest/acceptance/label/manual/manual_test.py similarity index 86% rename from allure-pytest/test/acceptance/label/manual/manual_test.py rename to tests/allure_pytest/acceptance/label/manual/manual_test.py index a04a315b..efac2362 100644 --- a/allure-pytest/test/acceptance/label/manual/manual_test.py +++ b/tests/allure_pytest/acceptance/label/manual/manual_test.py @@ -1,10 +1,11 @@ +""" ./allure-pytest/examples/label/manual/allure_manual.rst """ + from hamcrest import assert_that from allure_commons_test.report import has_test_case from allure_commons_test.label import has_label def test_allure_manual_label(executed_docstring_path): - """ ./examples/label/manual/allure_manual.rst """ assert_that(executed_docstring_path.allure_report, has_test_case("test_manual", has_label("ALLURE_MANUAL", True) @@ -13,7 +14,6 @@ def test_allure_manual_label(executed_docstring_path): def test_allure_manual_label_dynamic(executed_docstring_path): - """ ./examples/label/manual/allure_manual.rst """ assert_that(executed_docstring_path.allure_report, has_test_case("test_manual_dynamic", has_label("ALLURE_MANUAL", True) diff --git a/allure-pytest/test/acceptance/label/package/__init__.py b/tests/allure_pytest/acceptance/label/package/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/label/package/__init__.py rename to tests/allure_pytest/acceptance/label/package/__init__.py diff --git a/tests/allure_pytest/acceptance/label/package/regression_test.py b/tests/allure_pytest/acceptance/label/package/regression_test.py new file mode 100644 index 00000000..a53a1a98 --- /dev/null +++ b/tests/allure_pytest/acceptance/label/package/regression_test.py @@ -0,0 +1,48 @@ +import textwrap +from hamcrest import assert_that +from hamcrest import ends_with +from tests.conftest import AlluredTestdir +from allure_commons_test.report import has_test_case +from allure_commons_test.label import has_package + + +def test_path_with_dots_test(allured_testdir: AlluredTestdir): + path = allured_testdir.testdir.mkpydir("path.with.dots") + + path.joinpath("test_path.py").write_text( + textwrap.dedent( + """\ + def test_path_with_dots_test_example(): + pass + """ + ) + ) + + allured_testdir.run_with_allure() + + assert_that( + allured_testdir.allure_report, + has_test_case( + "test_path_with_dots_test_example", + has_package(ends_with("path.with.dots.test_path")) + ) + ) + + +def test_with_no_package(allured_testdir: AlluredTestdir): + """ + >>> def test_package_less(request): + ... pass + """ + allured_testdir.parse_docstring_source() + + allured_testdir.testdir.makeini("""[pytest]""") + allured_testdir.run_with_allure(allured_testdir.testdir.path) + + assert_that( + allured_testdir.allure_report, + has_test_case( + "test_package_less", + has_package("test_with_no_package") + ) + ) diff --git a/allure-pytest/test/acceptance/label/severity/__init__.py b/tests/allure_pytest/acceptance/label/severity/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/label/severity/__init__.py rename to tests/allure_pytest/acceptance/label/severity/__init__.py diff --git a/allure-pytest/test/acceptance/label/severity/class_severity_test.py b/tests/allure_pytest/acceptance/label/severity/class_severity_test.py similarity index 96% rename from allure-pytest/test/acceptance/label/severity/class_severity_test.py rename to tests/allure_pytest/acceptance/label/severity/class_severity_test.py index 6de97081..f6e330d8 100644 --- a/allure-pytest/test/acceptance/label/severity/class_severity_test.py +++ b/tests/allure_pytest/acceptance/label/severity/class_severity_test.py @@ -1,4 +1,4 @@ -"""./examples/label/severity/class_severity.rst""" +"""./allure-pytest/examples/label/severity/class_severity.rst""" from hamcrest import assert_that, all_of, is_not from allure_commons_test.report import has_test_case diff --git a/allure-pytest/test/acceptance/label/severity/module_severity_test.py b/tests/allure_pytest/acceptance/label/severity/module_severity_test.py similarity index 94% rename from allure-pytest/test/acceptance/label/severity/module_severity_test.py rename to tests/allure_pytest/acceptance/label/severity/module_severity_test.py index 9cde900a..81e77be5 100644 --- a/allure-pytest/test/acceptance/label/severity/module_severity_test.py +++ b/tests/allure_pytest/acceptance/label/severity/module_severity_test.py @@ -1,4 +1,4 @@ -""" ./examples/label/severity/module_severity.rst """ +""" ./allure-pytest/examples/label/severity/module_severity.rst """ from hamcrest import assert_that from allure_commons_test.report import has_test_case diff --git a/allure-pytest/test/acceptance/label/severity/select_severity_test.py b/tests/allure_pytest/acceptance/label/severity/select_severity_test.py similarity index 94% rename from allure-pytest/test/acceptance/label/severity/select_severity_test.py rename to tests/allure_pytest/acceptance/label/severity/select_severity_test.py index baed93fc..85fce270 100644 --- a/allure-pytest/test/acceptance/label/severity/select_severity_test.py +++ b/tests/allure_pytest/acceptance/label/severity/select_severity_test.py @@ -1,4 +1,4 @@ -""" ./examples/label/severity/select_tests_by_severity.rst """ +""" ./allure-pytest/examples/label/severity/select_tests_by_severity.rst """ import pytest from hamcrest import assert_that, only_contains, any_of, ends_with diff --git a/allure-pytest/test/acceptance/label/severity/severity_test.py b/tests/allure_pytest/acceptance/label/severity/severity_test.py similarity index 86% rename from allure-pytest/test/acceptance/label/severity/severity_test.py rename to tests/allure_pytest/acceptance/label/severity/severity_test.py index effb79b3..8391b31d 100644 --- a/allure-pytest/test/acceptance/label/severity/severity_test.py +++ b/tests/allure_pytest/acceptance/label/severity/severity_test.py @@ -1,11 +1,11 @@ +""" ./allure-pytest/examples/label/severity/severity.rst """ + from hamcrest import assert_that from allure_commons_test.report import has_test_case from allure_commons_test.label import has_severity def test_severity(executed_docstring_path): - """ ./examples/label/severity/severity.rst """ - assert_that(executed_docstring_path.allure_report, has_test_case("test_severity", has_severity("minor"), diff --git a/allure-pytest/test/acceptance/label/suite/__init__.py b/tests/allure_pytest/acceptance/label/suite/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/label/suite/__init__.py rename to tests/allure_pytest/acceptance/label/suite/__init__.py diff --git a/allure-pytest/test/acceptance/label/suite/custom_suite.py b/tests/allure_pytest/acceptance/label/suite/custom_suite.py similarity index 90% rename from allure-pytest/test/acceptance/label/suite/custom_suite.py rename to tests/allure_pytest/acceptance/label/suite/custom_suite.py index cccb210c..2914cc5d 100644 --- a/allure-pytest/test/acceptance/label/suite/custom_suite.py +++ b/tests/allure_pytest/acceptance/label/suite/custom_suite.py @@ -1,4 +1,4 @@ -""" ./examples/label/suite/custom_suite.rst """ +""" ./allure-pytest/examples/label/suite/custom_suite.rst """ from hamcrest import assert_that from allure_commons_test.report import has_test_case diff --git a/allure-pytest/test/acceptance/label/suite/default_suite_test.py b/tests/allure_pytest/acceptance/label/suite/default_suite_test.py similarity index 100% rename from allure-pytest/test/acceptance/label/suite/default_suite_test.py rename to tests/allure_pytest/acceptance/label/suite/default_suite_test.py diff --git a/allure-pytest/test/acceptance/label/suite/module_level_custom_suite_test.py b/tests/allure_pytest/acceptance/label/suite/module_level_custom_suite_test.py similarity index 85% rename from allure-pytest/test/acceptance/label/suite/module_level_custom_suite_test.py rename to tests/allure_pytest/acceptance/label/suite/module_level_custom_suite_test.py index 04e3fe7e..7db7d43e 100644 --- a/allure-pytest/test/acceptance/label/suite/module_level_custom_suite_test.py +++ b/tests/allure_pytest/acceptance/label/suite/module_level_custom_suite_test.py @@ -1,4 +1,4 @@ -""" ./examples/label/suite/module_level_custom_suite.rst """ +""" ./allure-pytest/examples/label/suite/module_level_custom_suite.rst """ from hamcrest import assert_that from allure_commons_test.report import has_test_case diff --git a/allure-pytest/test/acceptance/label/tag/__init__.py b/tests/allure_pytest/acceptance/label/tag/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/label/tag/__init__.py rename to tests/allure_pytest/acceptance/label/tag/__init__.py diff --git a/allure-pytest/test/acceptance/label/tag/tag_test.py b/tests/allure_pytest/acceptance/label/tag/tag_test.py similarity index 100% rename from allure-pytest/test/acceptance/label/tag/tag_test.py rename to tests/allure_pytest/acceptance/label/tag/tag_test.py diff --git a/allure-pytest/test/acceptance/link/__init__.py b/tests/allure_pytest/acceptance/link/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/link/__init__.py rename to tests/allure_pytest/acceptance/link/__init__.py diff --git a/allure-pytest/test/acceptance/link/dynamic_link_test.py b/tests/allure_pytest/acceptance/link/dynamic_link_test.py similarity index 96% rename from allure-pytest/test/acceptance/link/dynamic_link_test.py rename to tests/allure_pytest/acceptance/link/dynamic_link_test.py index 8fe6d610..90f33f01 100644 --- a/allure-pytest/test/acceptance/link/dynamic_link_test.py +++ b/tests/allure_pytest/acceptance/link/dynamic_link_test.py @@ -1,4 +1,4 @@ -""" ./examples/link/dynamic_link.rst """ +""" ./allure-pytest/examples/link/dynamic_link.rst """ import pytest from hamcrest import assert_that, equal_to diff --git a/tests/allure_pytest/acceptance/link/link_pattern_test.py b/tests/allure_pytest/acceptance/link/link_pattern_test.py new file mode 100644 index 00000000..a91b90c5 --- /dev/null +++ b/tests/allure_pytest/acceptance/link/link_pattern_test.py @@ -0,0 +1,26 @@ +from hamcrest import assert_that +from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_link, has_issue_link + + +def test_link_pattern(allured_testdir): + """ ./allure-pytest/examples/link/dynamic_link.rst """ + + allured_testdir.parse_docstring_path() + + allured_testdir.run_with_allure( + "--allure-link-pattern", + "issue:https://github.com/allure-framework/allure-python2/{}", + "--allure-link-pattern", + "docs:https://docs.qameta.io/{}" + ) + + assert_that( + allured_testdir.allure_report, + has_test_case( + "test_all_links_together", + has_issue_link("https://github.com/allure-framework/allure-python2/issues/24"), + has_issue_link("https://github.com/allure-framework/allure-python2/issues/24"), + has_link("https://docs.qameta.io/allure") + ) + ) diff --git a/allure-pytest/test/acceptance/link/link_test.py b/tests/allure_pytest/acceptance/link/link_test.py similarity index 96% rename from allure-pytest/test/acceptance/link/link_test.py rename to tests/allure_pytest/acceptance/link/link_test.py index 24f6868a..d359cc03 100644 --- a/allure-pytest/test/acceptance/link/link_test.py +++ b/tests/allure_pytest/acceptance/link/link_test.py @@ -1,4 +1,4 @@ -""" ./examples/link/link.rst """ +""" ./allure-pytest/examples/link/link.rst """ from hamcrest import assert_that from allure_commons_test.report import has_test_case diff --git a/allure-pytest/test/acceptance/parametrization/__init__.py b/tests/allure_pytest/acceptance/parametrization/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/parametrization/__init__.py rename to tests/allure_pytest/acceptance/parametrization/__init__.py diff --git a/allure-pytest/test/acceptance/parametrization/metafunc_test.py b/tests/allure_pytest/acceptance/parametrization/metafunc_test.py similarity index 100% rename from allure-pytest/test/acceptance/parametrization/metafunc_test.py rename to tests/allure_pytest/acceptance/parametrization/metafunc_test.py diff --git a/allure-pytest/test/acceptance/parametrization/parametrization_test.py b/tests/allure_pytest/acceptance/parametrization/parametrization_test.py similarity index 100% rename from allure-pytest/test/acceptance/parametrization/parametrization_test.py rename to tests/allure_pytest/acceptance/parametrization/parametrization_test.py diff --git a/allure-pytest/test/acceptance/status/__init__.py b/tests/allure_pytest/acceptance/status/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/status/__init__.py rename to tests/allure_pytest/acceptance/status/__init__.py diff --git a/allure-pytest/test/acceptance/status/base_call_status_test.py b/tests/allure_pytest/acceptance/status/base_call_status_test.py similarity index 100% rename from allure-pytest/test/acceptance/status/base_call_status_test.py rename to tests/allure_pytest/acceptance/status/base_call_status_test.py diff --git a/allure-pytest/test/acceptance/status/base_setup_status_test.py b/tests/allure_pytest/acceptance/status/base_setup_status_test.py similarity index 100% rename from allure-pytest/test/acceptance/status/base_setup_status_test.py rename to tests/allure_pytest/acceptance/status/base_setup_status_test.py diff --git a/allure-pytest/test/acceptance/status/base_step_status_test.py b/tests/allure_pytest/acceptance/status/base_step_status_test.py similarity index 100% rename from allure-pytest/test/acceptance/status/base_step_status_test.py rename to tests/allure_pytest/acceptance/status/base_step_status_test.py diff --git a/allure-pytest/test/acceptance/status/base_teardown_status_test.py b/tests/allure_pytest/acceptance/status/base_teardown_status_test.py similarity index 100% rename from allure-pytest/test/acceptance/status/base_teardown_status_test.py rename to tests/allure_pytest/acceptance/status/base_teardown_status_test.py diff --git a/allure-pytest/test/acceptance/status/skip_call_status_test.py b/tests/allure_pytest/acceptance/status/skip_call_status_test.py similarity index 100% rename from allure-pytest/test/acceptance/status/skip_call_status_test.py rename to tests/allure_pytest/acceptance/status/skip_call_status_test.py diff --git a/allure-pytest/test/acceptance/status/skip_setup_status_test.py b/tests/allure_pytest/acceptance/status/skip_setup_status_test.py similarity index 100% rename from allure-pytest/test/acceptance/status/skip_setup_status_test.py rename to tests/allure_pytest/acceptance/status/skip_setup_status_test.py diff --git a/allure-pytest/test/acceptance/status/skip_step_status_test.py b/tests/allure_pytest/acceptance/status/skip_step_status_test.py similarity index 100% rename from allure-pytest/test/acceptance/status/skip_step_status_test.py rename to tests/allure_pytest/acceptance/status/skip_step_status_test.py diff --git a/allure-pytest/test/acceptance/status/skip_teardown_status_test.py b/tests/allure_pytest/acceptance/status/skip_teardown_status_test.py similarity index 100% rename from allure-pytest/test/acceptance/status/skip_teardown_status_test.py rename to tests/allure_pytest/acceptance/status/skip_teardown_status_test.py diff --git a/allure-pytest/test/acceptance/status/xfail_call_status_test.py b/tests/allure_pytest/acceptance/status/xfail_call_status_test.py similarity index 100% rename from allure-pytest/test/acceptance/status/xfail_call_status_test.py rename to tests/allure_pytest/acceptance/status/xfail_call_status_test.py diff --git a/allure-pytest/test/acceptance/status/xfail_setup_status_test.py b/tests/allure_pytest/acceptance/status/xfail_setup_status_test.py similarity index 100% rename from allure-pytest/test/acceptance/status/xfail_setup_status_test.py rename to tests/allure_pytest/acceptance/status/xfail_setup_status_test.py diff --git a/allure-pytest/test/acceptance/status/xfail_step_status_test.py b/tests/allure_pytest/acceptance/status/xfail_step_status_test.py similarity index 100% rename from allure-pytest/test/acceptance/status/xfail_step_status_test.py rename to tests/allure_pytest/acceptance/status/xfail_step_status_test.py diff --git a/allure-pytest/test/acceptance/status/xfail_teardown_status_test.py b/tests/allure_pytest/acceptance/status/xfail_teardown_status_test.py similarity index 100% rename from allure-pytest/test/acceptance/status/xfail_teardown_status_test.py rename to tests/allure_pytest/acceptance/status/xfail_teardown_status_test.py diff --git a/allure-pytest/test/acceptance/step/__init__.py b/tests/allure_pytest/acceptance/step/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/step/__init__.py rename to tests/allure_pytest/acceptance/step/__init__.py diff --git a/allure-pytest/test/acceptance/step/outside_step_test.py b/tests/allure_pytest/acceptance/step/outside_step_test.py similarity index 100% rename from allure-pytest/test/acceptance/step/outside_step_test.py rename to tests/allure_pytest/acceptance/step/outside_step_test.py diff --git a/allure-pytest/test/acceptance/step/step_parameters.py b/tests/allure_pytest/acceptance/step/step_parameters.py similarity index 100% rename from allure-pytest/test/acceptance/step/step_parameters.py rename to tests/allure_pytest/acceptance/step/step_parameters.py diff --git a/tests/allure_pytest/acceptance/step/step_placeholder_test.py b/tests/allure_pytest/acceptance/step/step_placeholder_test.py new file mode 100644 index 00000000..fdb9c9f8 --- /dev/null +++ b/tests/allure_pytest/acceptance/step/step_placeholder_test.py @@ -0,0 +1,59 @@ +""" ./allure-pytest/examples/step/step_placeholder.rst """ +import pytest +from hamcrest import assert_that +from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_step +from allure_commons_test.result import has_status_details +from allure_commons_test.result import with_message_contains + + +def test_step_with_args_in_placeholder(executed_docstring_path): + assert_that( + executed_docstring_path.allure_report, + has_test_case( + "test_step_with_args_in_placeholder", + has_step("Step with two args: 'first' and 'second'") + ) + ) + + +def test_step_with_kwargs_in_placeholder(executed_docstring_path): + assert_that( + executed_docstring_path.allure_report, + has_test_case( + "test_step_with_kwargs_in_placeholder", + has_step("Step with two kwargs: '1' and 'second'") + ) + ) + + +def test_class_method_as_step(executed_docstring_path): + assert_that( + executed_docstring_path.allure_report, + has_test_case( + "test_class_method_as_step", + has_step("Class method step with 'first' and 'second'") + ) + ) + + +@pytest.mark.skip() +def test_args_less_than_placeholders(executed_docstring_source): + """ + >>> import allure + + >>> @allure.step("{0} and {1}") + ... def step(arg): + ... pass + + >>> def test_args_less_than_placeholders_example(): + ... step(0) + """ + + assert_that( + executed_docstring_source.allure_report, + has_test_case( + "test_args_less_than_placeholders_example", + has_status_details(with_message_contains("IndexError: tuple index out of range")) + ) + ) diff --git a/allure-pytest/test/acceptance/step/step_test.py b/tests/allure_pytest/acceptance/step/step_test.py similarity index 96% rename from allure-pytest/test/acceptance/step/step_test.py rename to tests/allure_pytest/acceptance/step/step_test.py index 75bd1668..f20f2fe6 100644 --- a/allure-pytest/test/acceptance/step/step_test.py +++ b/tests/allure_pytest/acceptance/step/step_test.py @@ -1,4 +1,4 @@ -""" ./examples/step/step.rst """ +""" ./allure-pytest/examples/step/step.rst """ from hamcrest import assert_that from allure_commons_test.report import has_test_case diff --git a/allure-pytest/test/acceptance/step/test_step_with_several_step_inside_thread.py b/tests/allure_pytest/acceptance/step/test_step_with_several_step_inside_thread.py similarity index 100% rename from allure-pytest/test/acceptance/step/test_step_with_several_step_inside_thread.py rename to tests/allure_pytest/acceptance/step/test_step_with_several_step_inside_thread.py diff --git a/tests/allure_pytest/acceptance/testplan/select_test_from_testplan_test.py b/tests/allure_pytest/acceptance/testplan/select_test_from_testplan_test.py new file mode 100644 index 00000000..19a69870 --- /dev/null +++ b/tests/allure_pytest/acceptance/testplan/select_test_from_testplan_test.py @@ -0,0 +1,115 @@ +import pytest +import json +import os +import inspect +from hamcrest import assert_that, contains_inanyorder, ends_with +from tests.conftest import AlluredTestdir +from typing import Sequence + + +@pytest.mark.parametrize( + ["planned_tests", "expected_tests"], + [ + pytest.param( + [{"id": 1}, {"id": 2}], + ["test_number_one", "test_number_two"], + id="ids-only" + ), + + pytest.param( + [{"id": 1}, {"id": 3}, {"id": 4}], + ["test_number_one", "test_number_three"], + id="id-for-test-with-two-ids" + ), + + pytest.param( + [{"id": 1234}], + [], + id="id-nomatch" + ), + + pytest.param( + [ + {"selector": "test_number_one"}, + {"selector": "test_number_three"} + ], + ["test_number_one", "test_number_three"], + id="selectors-only" + ), + + pytest.param( + [{"selector": "test_without_number"}], + ["test_without_number"], + id="selector-for-test-with-noid" + ), + + pytest.param( + [{"id": 2, "selector": "test_number_two"}], + ["test_number_two"], + id="id-selector-same-test" + ), + + pytest.param( + [{"selector": "test_without_never"}], + [], + id="selector-nomatch" + ), + + pytest.param( + None, + ["test_number_one", "test_number_two", "test_number_three", "test_without_number"], + id="noplan" + ), + ] +) +def test_select_by_testcase_id_test( + planned_tests: Sequence[dict], + expected_tests: Sequence[str], + allured_testdir: AlluredTestdir +): + """ + >>> import allure + + >>> @allure.id("1") + ... def test_number_one(): + ... pass + + >>> @allure.id("2") + ... def test_number_two(): + ... pass + + >>> @allure.id("3") + ... @allure.id("4") + ... def test_number_three(): + ... pass + + >>> def test_without_number(): + ... pass + """ + + test_name = inspect.currentframe().f_code.co_name + if planned_tests: + for item in planned_tests: + if "selector" in item: + selector = item['selector'] + item["selector"] = f"{test_name}#{selector}" + + testplan = {"tests": planned_tests} + py_path = allured_testdir.testdir.makefile(".json", json.dumps(testplan)) + os.environ["ALLURE_TESTPLAN_PATH"] = str(py_path) + else: + os.environ.pop("ALLURE_TESTPLAN_PATH", None) + + allured_testdir.parse_docstring_source() + allured_testdir.run_with_allure("--rootdir", allured_testdir.testdir.path) + + executed_full_names = [ + tc["fullName"] for tc in allured_testdir.allure_report.test_cases + ] + + assert_that( + executed_full_names, + contains_inanyorder( + * [ ends_with(name) for name in expected_tests ] + ) + ) diff --git a/allure-pytest/test/acceptance/unicode_identifier/__init__.py b/tests/allure_pytest/acceptance/unicode_identifier/__init__.py similarity index 100% rename from allure-pytest/test/acceptance/unicode_identifier/__init__.py rename to tests/allure_pytest/acceptance/unicode_identifier/__init__.py diff --git a/allure-pytest/test/acceptance/unicode_identifier/unicode_identifier_test.py b/tests/allure_pytest/acceptance/unicode_identifier/unicode_identifier_test.py similarity index 100% rename from allure-pytest/test/acceptance/unicode_identifier/unicode_identifier_test.py rename to tests/allure_pytest/acceptance/unicode_identifier/unicode_identifier_test.py diff --git a/allure-pytest/test/integration/__init__.py b/tests/allure_pytest/external_packages_support/__init__.py similarity index 100% rename from allure-pytest/test/integration/__init__.py rename to tests/allure_pytest/external_packages_support/__init__.py diff --git a/allure-pytest/test/integration/allure_ee/__init__.py b/tests/allure_pytest/external_packages_support/pytest_check/__init__.py similarity index 100% rename from allure-pytest/test/integration/allure_ee/__init__.py rename to tests/allure_pytest/external_packages_support/pytest_check/__init__.py diff --git a/allure-pytest/test/integration/pytest_check/pytest_check_test.py b/tests/allure_pytest/external_packages_support/pytest_check/pytest_check_test.py similarity index 99% rename from allure-pytest/test/integration/pytest_check/pytest_check_test.py rename to tests/allure_pytest/external_packages_support/pytest_check/pytest_check_test.py index 96b8f7d9..9babf102 100644 --- a/allure-pytest/test/integration/pytest_check/pytest_check_test.py +++ b/tests/allure_pytest/external_packages_support/pytest_check/pytest_check_test.py @@ -1,4 +1,3 @@ - import allure from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status, with_message_contains, has_status_details diff --git a/allure-pytest/test/integration/pytest_check/__init__.py b/tests/allure_pytest/external_packages_support/pytest_doctest/__init__.py similarity index 100% rename from allure-pytest/test/integration/pytest_check/__init__.py rename to tests/allure_pytest/external_packages_support/pytest_doctest/__init__.py diff --git a/allure-pytest/test/integration/pytest_doctest/pytest_doctest_test.py b/tests/allure_pytest/external_packages_support/pytest_doctest/pytest_doctest_test.py similarity index 100% rename from allure-pytest/test/integration/pytest_doctest/pytest_doctest_test.py rename to tests/allure_pytest/external_packages_support/pytest_doctest/pytest_doctest_test.py diff --git a/allure-pytest/test/integration/pytest_doctest/__init__.py b/tests/allure_pytest/external_packages_support/pytest_flakes/__init__.py similarity index 100% rename from allure-pytest/test/integration/pytest_doctest/__init__.py rename to tests/allure_pytest/external_packages_support/pytest_flakes/__init__.py diff --git a/allure-pytest/test/integration/pytest_flakes/pytest_flakes_test.py b/tests/allure_pytest/external_packages_support/pytest_flakes/pytest_flakes_test.py similarity index 100% rename from allure-pytest/test/integration/pytest_flakes/pytest_flakes_test.py rename to tests/allure_pytest/external_packages_support/pytest_flakes/pytest_flakes_test.py diff --git a/allure-pytest/test/integration/pytest_flakes/__init__.py b/tests/allure_pytest/external_packages_support/pytest_lazy_fixture/__init__.py similarity index 100% rename from allure-pytest/test/integration/pytest_flakes/__init__.py rename to tests/allure_pytest/external_packages_support/pytest_lazy_fixture/__init__.py diff --git a/allure-pytest/test/integration/pytest_lazy_fixture/pytest_lazy_fixture_test.py b/tests/allure_pytest/external_packages_support/pytest_lazy_fixture/pytest_lazy_fixture_test.py similarity index 100% rename from allure-pytest/test/integration/pytest_lazy_fixture/pytest_lazy_fixture_test.py rename to tests/allure_pytest/external_packages_support/pytest_lazy_fixture/pytest_lazy_fixture_test.py diff --git a/allure-pytest/test/integration/pytest_lazy_fixture/__init__.py b/tests/allure_pytest/external_packages_support/pytest_pluginmanager/__init__.py similarity index 100% rename from allure-pytest/test/integration/pytest_lazy_fixture/__init__.py rename to tests/allure_pytest/external_packages_support/pytest_pluginmanager/__init__.py diff --git a/allure-pytest/test/integration/pytest_pluginmanager/pytest_get_allure_plugin_test.py b/tests/allure_pytest/external_packages_support/pytest_pluginmanager/pytest_get_allure_plugin_test.py similarity index 100% rename from allure-pytest/test/integration/pytest_pluginmanager/pytest_get_allure_plugin_test.py rename to tests/allure_pytest/external_packages_support/pytest_pluginmanager/pytest_get_allure_plugin_test.py diff --git a/allure-pytest/test/integration/pytest_pluginmanager/__init__.py b/tests/allure_pytest/external_packages_support/pytest_rerunfailures/__init__.py similarity index 100% rename from allure-pytest/test/integration/pytest_pluginmanager/__init__.py rename to tests/allure_pytest/external_packages_support/pytest_rerunfailures/__init__.py diff --git a/allure-pytest/test/integration/pytest_rerunfailures/pytest_rerunfailures_test.py b/tests/allure_pytest/external_packages_support/pytest_rerunfailures/pytest_rerunfailures_test.py similarity index 100% rename from allure-pytest/test/integration/pytest_rerunfailures/pytest_rerunfailures_test.py rename to tests/allure_pytest/external_packages_support/pytest_rerunfailures/pytest_rerunfailures_test.py diff --git a/allure-pytest/test/integration/pytest_rerunfailures/__init__.py b/tests/allure_pytest/external_packages_support/pytest_xdist/__init__.py similarity index 100% rename from allure-pytest/test/integration/pytest_rerunfailures/__init__.py rename to tests/allure_pytest/external_packages_support/pytest_xdist/__init__.py diff --git a/allure-pytest/test/integration/pytest_xdist/pytest_xdist_select_test.py b/tests/allure_pytest/external_packages_support/pytest_xdist/pytest_xdist_select_test.py similarity index 100% rename from allure-pytest/test/integration/pytest_xdist/pytest_xdist_select_test.py rename to tests/allure_pytest/external_packages_support/pytest_xdist/pytest_xdist_select_test.py diff --git a/allure-pytest/test/integration/pytest_xdist/__init__.py b/tests/allure_pytest_bdd/__init__.py similarity index 100% rename from allure-pytest/test/integration/pytest_xdist/__init__.py rename to tests/allure_pytest_bdd/__init__.py diff --git a/tests/allure_pytest_bdd/acceptance/__init__.py b/tests/allure_pytest_bdd/acceptance/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_pytest_bdd/acceptance/scenario_outline_test.py b/tests/allure_pytest_bdd/acceptance/scenario_outline_test.py new file mode 100644 index 00000000..c194fb19 --- /dev/null +++ b/tests/allure_pytest_bdd/acceptance/scenario_outline_test.py @@ -0,0 +1,33 @@ +""" ./allure-pytest-bdd/examples/scenario-outline """ + +from hamcrest import assert_that, all_of +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status +from allure_commons_test.result import has_step +from allure_commons_test.result import has_parameter + + +def test_scenario_outline(executed_docstring_directory): + assert_that( + executed_docstring_directory.allure_report, + all_of( + has_test_case( + "Two examples with two parameters each", + with_status("passed"), + has_step("Given first step for Alpha value"), + has_step("When something is done with the value 1"), + has_step("Then check postconditions using Alpha and 1"), + has_parameter("first", "Alpha"), + has_parameter("second", "1") + ), + has_test_case( + "Two examples with two parameters each", + with_status("passed"), + has_step("Given first step for Bravo value"), + has_step("When something is done with the value 2"), + has_step("Then check postconditions using Bravo and 2"), + has_parameter("first", "Bravo"), + has_parameter("second", "2") + ) + ) + ) \ No newline at end of file diff --git a/tests/allure_pytest_bdd/acceptance/scenario_test.py b/tests/allure_pytest_bdd/acceptance/scenario_test.py new file mode 100644 index 00000000..14e93afa --- /dev/null +++ b/tests/allure_pytest_bdd/acceptance/scenario_test.py @@ -0,0 +1,21 @@ +""" ./allure-pytest-bdd/examples/simple-scenario """ + +from hamcrest import assert_that +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status +from allure_commons_test.result import has_step +from allure_commons_test.result import has_history_id + + +def test_simple_passed_scenario(executed_docstring_directory): + assert_that( + executed_docstring_directory.allure_report, + has_test_case( + "Simple passed example", + with_status("passed"), + has_step("Given the preconditions are satisfied"), + has_step("When the action is invoked"), + has_step("Then the postconditions are held"), + has_history_id() + ) + ) \ No newline at end of file diff --git a/tests/allure_pytest_bdd/conftest.py b/tests/allure_pytest_bdd/conftest.py new file mode 100644 index 00000000..13f8f55a --- /dev/null +++ b/tests/allure_pytest_bdd/conftest.py @@ -0,0 +1,7 @@ +import pytest +from tests.conftest import AlluredTestdir + +@pytest.fixture +def allured_testdir(allured_testdir: AlluredTestdir): + allured_testdir.select_plugins("allure_pytest_bdd") + return allured_testdir \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..d984e3f5 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,149 @@ +import pytest +import mock +import shutil +import runpy +from doctest import script_from_examples +from contextlib import contextmanager +from typing import Sequence + +import allure_commons +from allure_commons_test.report import AllureReport +from allure_commons.logger import AllureMemoryLogger + +pytest_plugins = "pytester" + + +def pytest_configure(config: pytest.Config): + config.addinivalue_line( + "markers", "real_logger: mark test to run with a real allure logger" + ) + + +@contextmanager +def fake_logger(): + blocked_plugins = [] + for name, plugin in allure_commons.plugin_manager.list_name_plugin(): + allure_commons.plugin_manager.unregister(plugin=plugin, name=name) + blocked_plugins.append(plugin) + + with mock.patch("allure_commons.logger.AllureFileLogger") as ReporterMock: + ReporterMock.return_value = AllureMemoryLogger() + yield ReporterMock.return_value + + for plugin in blocked_plugins: + allure_commons.plugin_manager.register(plugin) + + +def get_path_from_docstring(request: pytest.FixtureRequest): + return request.config.rootpath.joinpath( + ( + request.node.function.__doc__ or + request.node.module.__doc__ + ).strip() + ) + +class AlluredTestdir: + def __init__(self, pytester: pytest.Pytester, request: pytest.FixtureRequest): + self.testdir = pytester + self.request = request + self.allure_report = None + self.plugins = ["allure_pytest", "allure_pytest_bdd"] + self.enabled_plugins = {"allure_pytest"} + + def select_plugins(self, *plugins: str): + self.enabled_plugins = set(plugins) + + def get_plugin_args(self): + for plugin_package in self.plugins: + yield "-p" + if plugin_package in self.enabled_plugins: + yield plugin_package + else: + yield f"no:{plugin_package}" + + def parse_docstring_source(self): + docstring = self.request.node.function.__doc__ or self.request.node.module.__doc__ + source = script_from_examples(docstring).replace("#\n", "\n") + return self.testdir.makepyfile(source) + + def parse_docstring_path(self): + example_file = self.__get_example_path() + with open(example_file, encoding="utf-8") as f: + content = f.read() + source = script_from_examples(content) + return self.testdir.makepyfile(source) + + def copy_docstring_dir(self): + example_dir = get_path_from_docstring(self.request) + return shutil.copytree( + example_dir, + self.testdir.path, + symlinks=True, + dirs_exist_ok=True + ) + + def run_with_allure(self, *args: str, **kwargs: str): + pytest_args = [ + "--alluredir", + self.testdir.path, + *self.get_plugin_args(), + *args + ] + if self.request.node.get_closest_marker("real_logger"): + self.testdir.runpytest(*pytest_args, **kwargs) + self.allure_report = AllureReport(self.testdir.path) + else: + with fake_logger() as logger: + self.allure_report = logger + self.testdir.runpytest(*pytest_args, **kwargs) + + return self.allure_report + + def __get_example_path(self): + return self.request.config.rootdir.join( + ( + self.request.node.function.__doc__ or + self.request.node.module.__doc__ + ).strip() + ) + + +class AllureIntegrationRunner: + def __init__(self, module: str, entry_point :str = "main") -> None: + self.__module_name = module + self.__entry_point = entry_point + + def run_allure_integration(self, *args, **kwargs): + with fake_logger() as allure_results: + module = runpy.run_module(self.__module_name) + return_value = module[self.__entry_point](*args, **kwargs) + return { + "allure-results": allure_results, + "return-value": return_value + } + +@pytest.fixture +def allured_testdir(pytester: pytest.Pytester, request: pytest.FixtureRequest): + fixture = AlluredTestdir(pytester, request) + return fixture + + +@pytest.fixture +def executed_docstring_source(allured_testdir: AlluredTestdir): + allured_testdir.parse_docstring_source() + allured_testdir.run_with_allure() + return allured_testdir + + +@pytest.fixture +def executed_docstring_path(allured_testdir: AlluredTestdir): + allured_testdir.parse_docstring_path() + allured_testdir.run_with_allure() + return allured_testdir + + +@pytest.fixture +def executed_docstring_directory(allured_testdir: AlluredTestdir): + allured_testdir.copy_docstring_dir() + allured_testdir.run_with_allure() + return allured_testdir From a6a5d0cee11ac6d174ae5952a704110480a00046 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Wed, 8 Feb 2023 01:37:29 +0700 Subject: [PATCH 02/18] Implement rst-doc code block based examples testing --- allure-behave/examples/description.rst | 114 ++++++++ allure-behave/examples/hook.feature | 248 ---------------- allure-behave/examples/label.feature | 14 - allure-behave/examples/label.rst | 17 ++ allure-python-commons-test/src/container.py | 31 +- allure-python-commons-test/src/label.py | 16 +- allure-python-commons-test/src/result.py | 41 ++- .../acceptance/background/background.feature | 11 - .../acceptance/background/background_steps.py | 11 +- .../acceptance/background/background_test.py | 85 +++--- .../description/description_test.py | 63 ++-- .../acceptance/hooks/hook_test.py | 234 +++++++++++++++ .../hooks/test-data/attachment-hook.feature | 6 + .../hooks/test-data/attachment-hooks.py | 11 + .../hooks/test-data/context-step-hooks.py | 13 + .../hooks/test-data/feature-tag-hook.feature | 4 + .../hooks/test-data/func-step-hooks.py | 16 + .../hooks/test-data/global-hooks.feature | 3 + .../hooks/test-data/global-hooks.py | 33 +++ .../hooks/test-data/step-hook.feature | 3 + .../acceptance/hooks/test-data/steps.py | 5 + .../hooks/test-data/tag-hook.feature | 8 + .../acceptance/hooks/test-data/tag-hooks.py | 9 + .../acceptance/labels/label_test.py | 21 ++ tests/allure_behave/conftest.py | 276 ++++++++++++++---- tests/conftest.py | 185 +++++++++++- tests/requirements.txt | 3 + 27 files changed, 1039 insertions(+), 442 deletions(-) create mode 100644 allure-behave/examples/description.rst delete mode 100644 allure-behave/examples/hook.feature delete mode 100644 allure-behave/examples/label.feature create mode 100644 allure-behave/examples/label.rst delete mode 100644 tests/allure_behave/acceptance/background/background.feature create mode 100644 tests/allure_behave/acceptance/hooks/hook_test.py create mode 100644 tests/allure_behave/acceptance/hooks/test-data/attachment-hook.feature create mode 100644 tests/allure_behave/acceptance/hooks/test-data/attachment-hooks.py create mode 100644 tests/allure_behave/acceptance/hooks/test-data/context-step-hooks.py create mode 100644 tests/allure_behave/acceptance/hooks/test-data/feature-tag-hook.feature create mode 100644 tests/allure_behave/acceptance/hooks/test-data/func-step-hooks.py create mode 100644 tests/allure_behave/acceptance/hooks/test-data/global-hooks.feature create mode 100644 tests/allure_behave/acceptance/hooks/test-data/global-hooks.py create mode 100644 tests/allure_behave/acceptance/hooks/test-data/step-hook.feature create mode 100644 tests/allure_behave/acceptance/hooks/test-data/steps.py create mode 100644 tests/allure_behave/acceptance/hooks/test-data/tag-hook.feature create mode 100644 tests/allure_behave/acceptance/hooks/test-data/tag-hooks.py create mode 100644 tests/allure_behave/acceptance/labels/label_test.py create mode 100644 tests/requirements.txt diff --git a/allure-behave/examples/description.rst b/allure-behave/examples/description.rst new file mode 100644 index 00000000..b0a860d8 --- /dev/null +++ b/allure-behave/examples/description.rst @@ -0,0 +1,114 @@ +==================================== +Provide a description for a scenario +==================================== + +Scenario description can be added in various ways: + +#. In a .feature file +#. Dynamically in a step definition +#. Dynamically in a hook (e.g., before_scenario or after_scenario) + +----------------------------- +Description in a feature file +----------------------------- + +The easiest way to add a description to a test is to specify it directly in the +corresponding scenario in the .feature file. For example: + +.. code-block:: gherkin + :name: description-in-feature-feature + + Feature: Allure description for behave tests + Scenario: Description from a .feature file + This scenario has a description. + This description spans across multiple lines. + + Given noop + +The step definition is trivial: + +.. code-block:: python + :name: description-in-feature-steps + + from behave import given + + @given("noop") + def step_impl(context): + pass + +------------------- +Dynamic description +------------------- + +A description can be specified dynamically with the +:code:`allure.dynamic.description` function. This is useful if you want to +include runtime values in the description. + + +Description in a step definition +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Let's suppose, we want to add a description to the following test: + +.. code-block:: gherkin + :name: description-in-step-feature + + Feature: Allure description for behave tests + Scenario: Description from a step definition + Given description is provided in a step definition + +We can achieve that using the following step definition: + +.. code-block:: python + :name: description-in-step-steps + + from behave import given + import allure + + @given("description is provided in a step definition") + def step_impl(context): + allure.dynamic.description( + "This scenario has a description specified by the step definition" + ) + + +Description in a hook +^^^^^^^^^^^^^^^^^^^^^ + +It's also possible to add a description from a hook in the +:code:`environment.py` file. + +Suppose we have the following feature file (and step definition is the same as +in `Description in a feature file`_): + +.. code-block:: gherkin + :name: description-in-hook-feature + + Feature: Allure description for behave tests + Scenario: Description from the before_scenario hook + Given noop + + Scenario: Description from the after_scenario hook + Given noop + +We can provide a description in the :code:`environment.py` like this: + +.. code-block:: python + :name: description-in-hook-env + + import allure + + def before_scenario(context, scenario): + if "before_scenario" in scenario.name: + allure.dynamic.description( + "This scenario has a description specified in the " + "before_scenario hook" + ) + + + def after_scenario(context, scenario): + if "after_scenario" in scenario.name: + allure.dynamic.description( + "This scenario has a description specified in the " + "after_scenario hook" + ) diff --git a/allure-behave/examples/hook.feature b/allure-behave/examples/hook.feature deleted file mode 100644 index ffeda152..00000000 --- a/allure-behave/examples/hook.feature +++ /dev/null @@ -1,248 +0,0 @@ -Feature: Hook - - Scenario Outline: Hook 'scenario' - Given feature definition - """ - Feature: Hook - Scenario: Scenario with " " hook - Given simple passed step - - Scenario: Another scenario with " " hook - Given simple passed step - """ - And hooks implementation - """ - import allure_commons - - - @allure_commons.fixture - def _(context, ): - pass - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with " " hook" - And this scenario has fixture " " - - Then allure report has a scenario with name "Another scenario with " " hook" - And this scenario has fixture " " - - Examples: fixtures - | when | where | - | before | scenario | - | after | scenario | - - - Scenario Outline: Hook 'tag' - Given feature definition - """ - Feature: Hook - - @tag_for_hook - Scenario: Scenario with " " hook - Given simple passed step - - Scenario: Another scenario without " " hook - Given simple passed step - """ - And hooks implementation - """ - import allure_commons - - - @allure_commons.fixture - def _(context, ): - pass - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with " " hook" - And this scenario has fixture " @tag_for_hook" - - Then allure report has a scenario with name "Another scenario without " " hook" - And this scenario has not fixture " @tag_for_hook" - - Examples: fixtures - | when | where | - | before | tag | - | after | tag | - - - Scenario Outline: Hook 'tag' - Given feature definition - """ - @tag_for_hook - Feature: Hook - - Scenario: Scenario with " " hook - Given simple passed step - - Scenario: Another scenario with " " hook - Given simple passed step - """ - And hooks implementation - """ - import allure_commons - - - @allure_commons.fixture - def _(context, ): - pass - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with " " hook" - And this scenario has fixture " @tag_for_hook" - - Then allure report has a scenario with name "Another scenario with " " hook" - And this scenario has fixture " @tag_for_hook" - - Examples: fixtures - | when | where | - | before | tag | - | after | tag | - - - Scenario Outline: Hook 'feature' - Given feature definition - """ - Feature: Hook - Scenario: Scenario with " " hook - Given simple passed step - - Scenario: Another scenario with " " hook - Given simple passed step - """ - And hooks implementation - """ - import allure_commons - - - @allure_commons.fixture - def _(context, ): - pass - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with " " hook" - And this scenario has fixture " " - - Then allure report has a scenario with name "Another scenario with " " hook" - And this scenario has fixture " " - - Examples: fixtures - | when | where | - | before | feature | - | after | feature | - - - Scenario Outline: Hook 'all' - Given feature definition - """ - Feature: Hook - Scenario: Scenario with " " hook - Given simple passed step - - Scenario: Another scenario with " " hook - Given simple passed step - """ - And hooks implementation - """ - import allure_commons - - - @allure_commons.fixture - def _(context): - pass - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with " " hook" - And this scenario has fixture " " - - Then allure report has a scenario with name "Another scenario with " " hook" - And this scenario has fixture " " - - Examples: fixtures - | when | where | - | before | all | - | after | all | - - - Scenario: Hook attachment - Given feature definition - """ - Feature: Hook - Scenario: Scenario with "before_feature" hook and attachment - Given simple passed step - """ - And hooks implementation - """ - import allure - import allure_commons - - - @allure_commons.fixture - def before_feature(context, feature): - allure.attach('Hi there!', name='user attachment', attachment_type=allure.attachment_type.TEXT) - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with "before_feature" hook and attachment" - And this scenario has before fixture "before feature" - And this before has attachment - - - Scenario Outline: Hook step as context - Given feature definition - """ - Feature: Hook - Scenario: Scenario with " " hook and step inside - Given simple passed step - """ - And hooks implementation - """ - import allure - import allure_commons - - - @allure_commons.fixture - def _(context, ): - with allure.step('Step inside fixture'): - pass - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with " " hook and step inside" - And this scenario has fixture " " - And this contains step "Step inside fixture" - - Examples: fixtures - | when | where | - | before | scenario | - | after | scenario | - - - Scenario Outline: Hook step as function - Given feature definition - """ - Feature: Hook - Scenario: Scenario with " " hook and step as function inside - Given simple passed step - """ - And hooks implementation - """ - import allure - import allure_commons - - @allure.step('Step function') - def step(): - pass - - @allure_commons.fixture - def _(context): - step() - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with " " hook and step as function inside" - And this scenario has fixture " " - And this contains step "Step function" - - Examples: fixtures - | when | where | - | before | all | - | after | all | - diff --git a/allure-behave/examples/label.feature b/allure-behave/examples/label.feature deleted file mode 100644 index 49aa9065..00000000 --- a/allure-behave/examples/label.feature +++ /dev/null @@ -1,14 +0,0 @@ -Feature: Label - - Scenario: Scenario label - Given feature definition - """ - Feature: Step status - - @allure.label.owner:me - Scenario: Scenario with passed step - Given simple passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with passed step" - And scenario has "owner" label with value "me" \ No newline at end of file diff --git a/allure-behave/examples/label.rst b/allure-behave/examples/label.rst new file mode 100644 index 00000000..31a3e07a --- /dev/null +++ b/allure-behave/examples/label.rst @@ -0,0 +1,17 @@ +================================= +Add a custom label for a scenario +================================= + +It's possible to add a custom label to a behave scenario. Simply apply +:code:`@allure.label.:` tag to your scenario, e.g.: + +.. code:: gherkin + :name: label-feature + + Feature: Allure label for behave tests + @allure.label.author:John-Doe + Scenario: Scenario marked with an author label + Given noop + +Note, that neither the name nor the value of a label, added that way, cannot +contain whitespaces. diff --git a/allure-python-commons-test/src/container.py b/allure-python-commons-test/src/container.py index 894bd60a..50aa1e6b 100644 --- a/allure-python-commons-test/src/container.py +++ b/allure-python-commons-test/src/container.py @@ -10,13 +10,14 @@ def __init__(self, report, *matchers): self.matchers = matchers def _matches(self, item): - return has_property('test_containers', - has_item( - all_of( - has_entry('children', has_item(item['uuid'])), - *self.matchers - ) - )).matches(self.report) + return has_property( + 'test_containers', + has_item( + all_of( + has_entry('children', has_item(item['uuid'])), + *self.matchers + ) + )).matches(self.report) def describe_to(self, description): description.append_text('describe me later').append_list('[', ', ', ']', self.matchers) @@ -151,13 +152,15 @@ def has_same_container(*args): def has_fixture(section, name, *matchers): - return has_entry(section, - has_item( - all_of( - has_entry('name', equal_to(name)), - *matchers - ) - )) + return has_entry( + section, + has_item( + all_of( + has_entry('name', equal_to(name)), + *matchers + ) + ) + ) def has_before(name, *matchers): diff --git a/allure-python-commons-test/src/label.py b/allure-python-commons-test/src/label.py index cfc82654..9a3c9dc8 100644 --- a/allure-python-commons-test/src/label.py +++ b/allure-python-commons-test/src/label.py @@ -3,13 +3,15 @@ def has_label(name, value): - return has_entry('labels', - has_item( - all_of( - has_entry('name', name), - has_entry('value', value) - ) - )) + return has_entry( + 'labels', + has_item( + all_of( + has_entry('name', name), + has_entry('value', value) + ) + ) + ) def has_severity(level): diff --git a/allure-python-commons-test/src/result.py b/allure-python-commons-test/src/result.py index 87ee0e0e..92df5542 100644 --- a/allure-python-commons-test/src/result.py +++ b/allure-python-commons-test/src/result.py @@ -82,13 +82,15 @@ def has_description_html(*matchers): def has_step(name, *matchers): - return has_entry('steps', - has_item( - all_of( - has_entry('name', equal_to(name)), - *matchers - ) - )) + return has_entry( + 'steps', + has_item( + all_of( + has_entry('name', equal_to(name)), + *matchers + ) + ) + ) def has_parameter(name, value, *matchers): @@ -130,17 +132,24 @@ def has_test_case_link(url, name=None): def has_attachment(attach_type=None, name=None): - return has_entry('attachments', - has_item( - all_of( - has_entry('source', anything()), - has_entry('type', attach_type) if attach_type else anything(), - has_entry('name', name) if name else anything() - ) - )) + return has_entry( + 'attachments', + has_item( + all_of( + has_entry('source', anything()), + has_entry('type', attach_type) if attach_type else anything(), + has_entry('name', name) if name else anything() + ) + ) + ) -def has_attachment_with_content(attachments, content_matcher, attach_type=None, name=None): +def has_attachment_with_content( + attachments, + content_matcher, + attach_type=None, + name=None +): return has_entry( 'attachments', has_item( diff --git a/tests/allure_behave/acceptance/background/background.feature b/tests/allure_behave/acceptance/background/background.feature deleted file mode 100644 index 7f6ae0e9..00000000 --- a/tests/allure_behave/acceptance/background/background.feature +++ /dev/null @@ -1,11 +0,0 @@ -Feature: Allure-behave compatibility with feature backgrounds - Background: A background with {bg_step_status} step - Given the first background step that is {bg_step_status} - And the second background step with no failures - - Scenario: Scenario with background containing {bg_step_status} step - Given the first step with no failures - And the second step with no failures - - Scenario: Another scenario with background containing {bg_step_status} step - Given the step with no failures diff --git a/tests/allure_behave/acceptance/background/background_steps.py b/tests/allure_behave/acceptance/background/background_steps.py index db066e80..a2865174 100644 --- a/tests/allure_behave/acceptance/background/background_steps.py +++ b/tests/allure_behave/acceptance/background/background_steps.py @@ -1,23 +1,20 @@ from behave import given @given("the first background step that is passed") -def the_first_background_step_that_is_passed(*args, **kwargs): +def step_impl(_): pass - @given("the first background step that is failed") -def the_first_background_step_that_is_failed(*args, **kwargs): +def step_impl(_): assert False, "Failed assertion message" - @given("the first background step that is broken") -def the_first_background_step_that_is_broken(*args, **kwargs): +def step_impl(_): raise ValueError("Something is broken") - @given("the second background step with no failures") @given("the first step with no failures") @given("the second step with no failures") @given("the step with no failures") -def the_step_with_no_failures(*args, **kwargs): +def step_impl(_): pass diff --git a/tests/allure_behave/acceptance/background/background_test.py b/tests/allure_behave/acceptance/background/background_test.py index 68bf2324..4aa3e540 100644 --- a/tests/allure_behave/acceptance/background/background_test.py +++ b/tests/allure_behave/acceptance/background/background_test.py @@ -1,6 +1,5 @@ import pytest -from behave.parser import Parser -from hamcrest import assert_that, all_of +from hamcrest import assert_that from tests.allure_behave.conftest import AllureBehaveRunner from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status @@ -17,54 +16,64 @@ ] ) def test_background( - allure_behave_runner: AllureBehaveRunner, + behave_runner: AllureBehaveRunner, step_outcome: str, status: str, remained_steps_status: str ): - allure_behave_runner.run_feature_of_current_test( - bg_step_status=step_outcome + feature = \ + F"""Feature: Allure-behave compatibility with feature backgrounds + Background: A background with {step_outcome} step + Given the first background step that is {step_outcome} + And the second background step with no failures + + Scenario: Scenario with background containing {step_outcome} step + Given the first step with no failures + And the second step with no failures + + Scenario: Another scenario with background containing {step_outcome} step + Given the step with no failures + """ + behave_runner.run_behave( + features=[feature], + step_paths=["./background_steps.py"] ) assert_that( - allure_behave_runner.allure_results, + behave_runner.allure_results, has_test_case( f"Scenario with background containing {step_outcome} step", - all_of( - with_status(status), - has_step( - f"Given the first background step that is {step_outcome}", - with_status(status) - ), - has_step( - "And the second background step with no failures", - with_status(remained_steps_status) - ), - has_step( - "Given the first step with no failures", - with_status(remained_steps_status) - ), - has_step( - "And the second step with no failures", - with_status(remained_steps_status) - ) + with_status(status), + has_step( + f"Given the first background step that is {step_outcome}", + with_status(status) + ), + has_step( + "And the second background step with no failures", + with_status(remained_steps_status) + ), + has_step( + "Given the first step with no failures", + with_status(remained_steps_status) + ), + has_step( + "And the second step with no failures", + with_status(remained_steps_status) ) ), has_test_case( f"Another scenario with background containing {step_outcome} step", - all_of( - with_status(status), - has_step( - f"Given the first background step that is {step_outcome}", - with_status(status) - ), - has_step( - "And the second background step with no failures", - with_status(remained_steps_status) - ), - has_step( - "Given the step with no failures", - with_status(remained_steps_status) - ) + with_status(status), + has_step( + f"Given the first background step that is {step_outcome}", + with_status(status) + ), + has_step( + "And the second background step with no failures", + with_status(remained_steps_status) + ), + has_step( + "Given the step with no failures", + with_status(remained_steps_status) ) ) ) \ No newline at end of file diff --git a/tests/allure_behave/acceptance/description/description_test.py b/tests/allure_behave/acceptance/description/description_test.py index 7c63c4d0..bcf26e9d 100644 --- a/tests/allure_behave/acceptance/description/description_test.py +++ b/tests/allure_behave/acceptance/description/description_test.py @@ -1,52 +1,79 @@ -""" ./allure-behave/examples/description """ +""" ./allure-behave/examples/description.rst """ from tests.allure_behave.conftest import AllureBehaveRunner -from hamcrest import assert_that, all_of +from hamcrest import assert_that from allure_commons_test.report import has_test_case from allure_commons_test.result import has_description +from allure_commons_test.result import with_status - -def test_descriptions_from_feature_file(executed_docstring_path: AllureBehaveRunner): +def test_descriptions_from_feature_file(behave_runner: AllureBehaveRunner): + behave_runner.run_rst_example( + "description-in-feature-feature", + steps=["description-in-feature-steps"] + ) assert_that( - executed_docstring_path.allure_results, + behave_runner.allure_results, has_test_case( "Description from a .feature file", - has_description("This scenario has a description.\nThis description spans across multiple lines.") + with_status("passed"), + has_description( + "This scenario has a description.\nThis description spans " + "across multiple lines." + ) ) ) -def test_descriptions_from_step(executed_docstring_path: AllureBehaveRunner): +def test_descriptions_from_step(behave_runner: AllureBehaveRunner): + behave_runner.run_rst_example( + "description-in-step-feature", + steps=["description-in-step-steps"] + ) assert_that( - executed_docstring_path.allure_results, + behave_runner.allure_results, has_test_case( - "Description from a step definition function", + "Description from a step definition", + with_status("passed"), has_description( - "This scenario has a description specified by a step definition" + "This scenario has a description specified by the step " + "definition" ) ) ) -def test_descriptions_before_scenario(executed_docstring_path: AllureBehaveRunner): +def test_descriptions_before_scenario(behave_runner: AllureBehaveRunner): + behave_runner.run_rst_example( + "description-in-hook-feature", + steps=["description-in-feature-steps"], + environment="description-in-hook-env" + ) assert_that( - executed_docstring_path.allure_results, + behave_runner.allure_results, has_test_case( - "Description from before_scenario hook", + "Description from the before_scenario hook", + with_status("passed"), has_description( - "This scenario has a description specified in before_scenario hook" + "This scenario has a description specified in the before_scenario hook" ) ) ) -def test_descriptions_after_scenario(executed_docstring_path: AllureBehaveRunner): +def test_descriptions_after_scenario(behave_runner: AllureBehaveRunner): + behave_runner.run_rst_example( + "description-in-hook-feature", + steps=["description-in-feature-steps"], + environment="description-in-hook-env" + ) assert_that( - executed_docstring_path.allure_results, + behave_runner.allure_results, has_test_case( - "Description from after_scenario hook", + "Description from the after_scenario hook", + with_status("passed"), has_description( - "This scenario has a description specified in after_scenario hook" + "This scenario has a description specified in the " + "after_scenario hook" ) ) ) diff --git a/tests/allure_behave/acceptance/hooks/hook_test.py b/tests/allure_behave/acceptance/hooks/hook_test.py new file mode 100644 index 00000000..537ccb39 --- /dev/null +++ b/tests/allure_behave/acceptance/hooks/hook_test.py @@ -0,0 +1,234 @@ +import allure +from tests.allure_behave.conftest import AllureBehaveRunner as Runner +from hamcrest import assert_that, all_of, not_, equal_to +from allure_commons_test.container import has_container, has_before, has_after +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status +from allure_commons_test.result import has_attachment_with_content +from allure_commons_test.result import has_step + + +def test_global_hooks(behave_runner: Runner): + behave_runner.run_behave( + feature_paths=["./test-data/global-hooks.feature"], + step_paths=["./test-data/steps.py"], + environment_path="./test-data/global-hooks.py" + ) + assert_that( + behave_runner.allure_results, + has_test_case( + "Global hooks as fixtures", + with_status("passed"), + has_container( + behave_runner.allure_results, + has_before("before all", with_status("passed")) + ), + has_container( + behave_runner.allure_results, + has_before("before feature", with_status("passed")) + ), + has_container( + behave_runner.allure_results, + has_before("before scenario", with_status("passed")) + ), + has_container( + behave_runner.allure_results, + has_before("before step", with_status("passed")) + ), + has_container( + behave_runner.allure_results, + has_after("after all", with_status("passed")) + ), + has_container( + behave_runner.allure_results, + has_after("after feature", with_status("passed")) + ), + has_container( + behave_runner.allure_results, + has_after("after scenario", with_status("passed")) + ), + has_container( + behave_runner.allure_results, + has_after("after step", with_status("passed")) + ) + ) + ) + +def test_tag_hooks(behave_runner: Runner): + behave_runner.run_behave( + feature_paths=[ + "./test-data/tag-hook.feature", + "./test-data/feature-tag-hook.feature" + ], + step_paths=["./test-data/steps.py"], + environment_path="./test-data/tag-hooks.py" + ) + assert_that( + behave_runner.allure_results, + all_of( + has_test_case( + "Tag hooks as fixture - this scenario is affected", + with_status("passed"), + has_container( + behave_runner.allure_results, + has_before("before tag @hook_target", with_status("passed")) + ), + has_container( + behave_runner.allure_results, + has_after("after tag @hook_target", with_status("passed")) + ) + ), + has_test_case( + "Tag hooks as fixture - this scenario is not affected", + with_status("passed"), + not_( + has_container( + behave_runner.allure_results, + has_before("before tag @hook_target") + ) + ), + not_( + has_container( + behave_runner.allure_results, + has_after("after tag @hook_target") + ) + ) + ), + has_test_case( + "Feature-level tag hooks as fixtures", + with_status("passed"), + has_container( + behave_runner.allure_results, + has_before("before tag @hook_target", with_status("passed")) + ), + has_container( + behave_runner.allure_results, + has_after("after tag @hook_target", with_status("passed")) + ) + ) + ) + ) + +def test_attachment_before_feature(behave_runner: Runner): + behave_runner.run_behave( + feature_paths=["./test-data/attachment-hook.feature"], + step_paths=["./test-data/steps.py"], + environment_path="./test-data/attachment-hooks.py" + ) + assert_that( + behave_runner.allure_results, + all_of( + has_test_case( + "Attachment from before_feature fixture-hook", + with_status("passed"), + has_container( + behave_runner.allure_results, + has_before( + "before feature", + with_status("passed"), + has_attachment_with_content( + behave_runner.allure_results.attachments, + equal_to("Attachment from before_feature"), + allure.attachment_type.TEXT.mime_type, + "Dynamic attachment" + ) + ), + ) + ), + has_test_case( + "One more scenario with same attachment in fixture-hook", + with_status("passed"), + has_container( + behave_runner.allure_results, + has_before( + "before feature", + with_status("passed"), + has_attachment_with_content( + behave_runner.allure_results.attachments, + equal_to("Attachment from before_feature"), + allure.attachment_type.TEXT.mime_type, + "Dynamic attachment" + ) + ) + ) + ) + ) + ) + + +def test_context_step_in_scenario_hooks(behave_runner: Runner): + behave_runner.run_behave( + feature_paths=["./test-data/step-hook.feature"], + step_paths=["./test-data/steps.py"], + environment_path="./test-data/context-step-hooks.py" + ) + assert_that( + behave_runner.allure_results, + all_of( + has_test_case( + "Hook with steps", + with_status("passed"), + has_container( + behave_runner.allure_results, + has_before( + "before scenario", + with_status("passed"), + has_step( + "Step in before_scenario", + with_status("passed") + ) + ), + ), + has_container( + behave_runner.allure_results, + has_after( + "after scenario", + with_status("passed"), + has_step( + "Step in after_scenario", + with_status("passed") + ) + ) + ) + ) + ) + ) + + +def test_func_step_in_scenario_hooks(behave_runner: Runner): + behave_runner.run_behave( + feature_paths=["./test-data/step-hook.feature"], + step_paths=["./test-data/steps.py"], + environment_path="./test-data/func-step-hooks.py" + ) + assert_that( + behave_runner.allure_results, + all_of( + has_test_case( + "Hook with steps", + with_status("passed"), + has_container( + behave_runner.allure_results, + has_before( + "before all", + with_status("passed"), + has_step( + "Step in 'before_all'", + with_status("passed") + ) + ), + ), + has_container( + behave_runner.allure_results, + has_after( + "after all", + with_status("passed"), + has_step( + "Step in 'after_all'", + with_status("passed") + ) + ) + ) + ) + ) + ) \ No newline at end of file diff --git a/tests/allure_behave/acceptance/hooks/test-data/attachment-hook.feature b/tests/allure_behave/acceptance/hooks/test-data/attachment-hook.feature new file mode 100644 index 00000000..6b66a2c6 --- /dev/null +++ b/tests/allure_behave/acceptance/hooks/test-data/attachment-hook.feature @@ -0,0 +1,6 @@ +Feature: Behave hook support + Scenario: Attachment from before_feature fixture-hook + Given noop + + Scenario: One more scenario with same attachment in fixture-hook + Given noop diff --git a/tests/allure_behave/acceptance/hooks/test-data/attachment-hooks.py b/tests/allure_behave/acceptance/hooks/test-data/attachment-hooks.py new file mode 100644 index 00000000..fb9a84f8 --- /dev/null +++ b/tests/allure_behave/acceptance/hooks/test-data/attachment-hooks.py @@ -0,0 +1,11 @@ +import allure +import allure_commons + + +@allure_commons.fixture +def before_feature(context, feature): + allure.attach( + "Attachment from before_feature", + name="Dynamic attachment", + attachment_type=allure.attachment_type.TEXT + ) diff --git a/tests/allure_behave/acceptance/hooks/test-data/context-step-hooks.py b/tests/allure_behave/acceptance/hooks/test-data/context-step-hooks.py new file mode 100644 index 00000000..689454e1 --- /dev/null +++ b/tests/allure_behave/acceptance/hooks/test-data/context-step-hooks.py @@ -0,0 +1,13 @@ +import allure +import allure_commons + +@allure_commons.fixture +def before_scenario(context, scenario): + with allure.step("Step in before_scenario"): + pass + + +@allure_commons.fixture +def after_scenario(context, scenario): + with allure.step("Step in after_scenario"): + pass diff --git a/tests/allure_behave/acceptance/hooks/test-data/feature-tag-hook.feature b/tests/allure_behave/acceptance/hooks/test-data/feature-tag-hook.feature new file mode 100644 index 00000000..d1bc54ee --- /dev/null +++ b/tests/allure_behave/acceptance/hooks/test-data/feature-tag-hook.feature @@ -0,0 +1,4 @@ +@hook_target +Feature: Behave hook support + Scenario: Feature-level tag hooks as fixtures + Given noop diff --git a/tests/allure_behave/acceptance/hooks/test-data/func-step-hooks.py b/tests/allure_behave/acceptance/hooks/test-data/func-step-hooks.py new file mode 100644 index 00000000..303485b4 --- /dev/null +++ b/tests/allure_behave/acceptance/hooks/test-data/func-step-hooks.py @@ -0,0 +1,16 @@ +import allure +import allure_commons + +@allure.step("Step in {caller}") +def step(caller): + pass + + +@allure_commons.fixture +def before_all(context): + step("before_all") + + +@allure_commons.fixture +def after_all(context): + step("after_all") diff --git a/tests/allure_behave/acceptance/hooks/test-data/global-hooks.feature b/tests/allure_behave/acceptance/hooks/test-data/global-hooks.feature new file mode 100644 index 00000000..441f10e7 --- /dev/null +++ b/tests/allure_behave/acceptance/hooks/test-data/global-hooks.feature @@ -0,0 +1,3 @@ +Feature: Behave hooks support + Scenario: Global hooks as fixtures + Given noop \ No newline at end of file diff --git a/tests/allure_behave/acceptance/hooks/test-data/global-hooks.py b/tests/allure_behave/acceptance/hooks/test-data/global-hooks.py new file mode 100644 index 00000000..1eb703c0 --- /dev/null +++ b/tests/allure_behave/acceptance/hooks/test-data/global-hooks.py @@ -0,0 +1,33 @@ +import allure_commons + +@allure_commons.fixture +def before_all(context): + pass + +@allure_commons.fixture +def after_all(context): + pass + +@allure_commons.fixture +def before_feature(context, feature): + pass + +@allure_commons.fixture +def after_feature(context, feature): + pass + +@allure_commons.fixture +def before_scenario(context, scenario): + pass + +@allure_commons.fixture +def after_scenario(context, scenario): + pass + +@allure_commons.fixture +def before_step(context, step): + pass + +@allure_commons.fixture +def after_step(context, step): + pass diff --git a/tests/allure_behave/acceptance/hooks/test-data/step-hook.feature b/tests/allure_behave/acceptance/hooks/test-data/step-hook.feature new file mode 100644 index 00000000..03e5f216 --- /dev/null +++ b/tests/allure_behave/acceptance/hooks/test-data/step-hook.feature @@ -0,0 +1,3 @@ +Feature: Behave hook support + Scenario: Hook with steps + Given noop diff --git a/tests/allure_behave/acceptance/hooks/test-data/steps.py b/tests/allure_behave/acceptance/hooks/test-data/steps.py new file mode 100644 index 00000000..254950d7 --- /dev/null +++ b/tests/allure_behave/acceptance/hooks/test-data/steps.py @@ -0,0 +1,5 @@ +from behave import given + +@given("noop") +def step_impl(context): + pass diff --git a/tests/allure_behave/acceptance/hooks/test-data/tag-hook.feature b/tests/allure_behave/acceptance/hooks/test-data/tag-hook.feature new file mode 100644 index 00000000..c537b7c6 --- /dev/null +++ b/tests/allure_behave/acceptance/hooks/test-data/tag-hook.feature @@ -0,0 +1,8 @@ +Feature: Behave hooks support + + @hook_target + Scenario: Tag hooks as fixture - this scenario is affected + Given noop + + Scenario: Tag hooks as fixture - this scenario is not affected + Given noop diff --git a/tests/allure_behave/acceptance/hooks/test-data/tag-hooks.py b/tests/allure_behave/acceptance/hooks/test-data/tag-hooks.py new file mode 100644 index 00000000..81048c1e --- /dev/null +++ b/tests/allure_behave/acceptance/hooks/test-data/tag-hooks.py @@ -0,0 +1,9 @@ +import allure_commons + +@allure_commons.fixture +def before_tag(context, tag): + pass + +@allure_commons.fixture +def after_tag(context, tag): + pass diff --git a/tests/allure_behave/acceptance/labels/label_test.py b/tests/allure_behave/acceptance/labels/label_test.py new file mode 100644 index 00000000..66c8c4dd --- /dev/null +++ b/tests/allure_behave/acceptance/labels/label_test.py @@ -0,0 +1,21 @@ +""" ./allure-behave/examples/label.rst """ + +from tests.allure_behave.conftest import AllureBehaveRunner +from hamcrest import assert_that, all_of +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status +from allure_commons_test.label import has_label + +def test_label_from_feature_file(behave_runner: AllureBehaveRunner): + behave_runner.run_rst_example( + "label-feature", + step_literals=["given('noop')(lambda c:None)"] + ) + assert_that( + behave_runner.allure_results, + has_test_case( + "Scenario marked with an author label", + with_status("passed"), + has_label("author", "John-Doe") + ) + ) diff --git a/tests/allure_behave/conftest.py b/tests/allure_behave/conftest.py index 238e184d..3fd79038 100644 --- a/tests/allure_behave/conftest.py +++ b/tests/allure_behave/conftest.py @@ -1,31 +1,50 @@ import shutil +import sys +import behave.step_registry +from itertools import chain +from pathlib import Path from pytest import FixtureRequest, fixture, Pytester from tests.conftest import AllureIntegrationRunner from tests.conftest import get_path_from_docstring +from tests.conftest import fake_logger +from tests.conftest import RstExampleTable from behave.runner import Runner -import behave.step_registry from behave.step_registry import StepRegistry +from typing import Sequence +from behave.configuration import Configuration +from behave.formatter.base import StreamOpener +from behave.runner import Context, Runner +from behave import matchers +from behave.step_registry import setup_step_decorators +from behave.parser import parse_feature +from behave.formatter.pretty import PrettyFormatter +from allure_behave.formatter import AllureFormatter - -def __fix_behave_multirun(): - # behave doesn't play nicely with consecutive programmatic runs, so we - # force it to do a proper reset here - original_run_model = Runner.run_model - def __fixed_run_model(self, *args, **kwargs): - # Originally, the runner caches the instance of step_registry on a - # module level and reuse that same registry on each run, resulting in - # disparity between step registration and step matching. - # Here we force the runner to use the newest instance of registry. - self.step_registry = behave.step_registry.registry - return original_run_model(self, *args, **kwargs) - Runner.run_model = __fixed_run_model - +def __fix_behave_in_memory_run(): + # Behave has poor support for consecutive prigrammatic runs. This is due to + # how step decorators are cached. + # There are three ways to introduce behave step decorators (i.e., @given) + # into a step definition module: + # 1. from behave import given (most common use) + # 2. Without any import (just as if they are globally defined, less + # common) + # 3. from behave.step_regostry import given (rarely) + # The decorators are associated with a StepRegistry instance. This + # association is first created when behave.step_registry module is imported. + # They are then introduced as the behave package attributes and basically + # are cached on a package level. Even if we replace the decorators from + # behave.step_registry with new ones associated with a new StepRegistry with + # the behave.step_registry.setup_step_decorators function, the decorators in + # the behave package remains the same and remains attached to the old step + # registry. + # We need to create a new StepRegistry before a run to prevent step + # duplication error, but if step definitions are created with the decorators + # introduced with (1), they will be added to the old registry and will be + # never matched. + # To fix that we force decorators to use the global instance of the + # StepRegistry. original_add_step_definition = StepRegistry.add_step_definition def __fixed_add_step_definition(self, *args, **kwargs): - # The same happens with step registration mechanism embedded into - # bdd declarators (given, then, etc). - # Here we redirect add_step_definition method to the newest instance of - # registry. return original_add_step_definition( behave.step_registry.registry, *args, @@ -33,6 +52,56 @@ def __fixed_add_step_definition(self, *args, **kwargs): ) StepRegistry.add_step_definition = __fixed_add_step_definition +class InMemoryBehaveRunner(Runner): + def __init__(self, features, steps, environment): + config = Configuration(["--no-snippets"], load_config=False) + super().__init__(config) + self.__features = features + self.__steps = steps + self.__environment = environment + + def load_hooks(self, filename=None): + if self.__environment: + exec(self.__environment, self.hooks) + if "before_all" not in self.hooks: + self.hooks["before_all"] = self.before_all_default_hook + + def load_step_definitions(self, extra_step_paths=None): + behave.step_registry.registry = self.step_registry = StepRegistry() + step_globals = { + "use_step_matcher": matchers.use_step_matcher, + "step_matcher": matchers.step_matcher, + } + + # To support the decorators (i.e., @given) with no imports + setup_step_decorators(step_globals, self.step_registry) + + default_matcher = matchers.current_matcher + for step in self.__steps: + step_module_globals = step_globals.copy() + exec(step, step_module_globals) + matchers.current_matcher = default_matcher + + def load_features(self): + self.features.extend( + parse_feature(f) for f in self.__features + ) + + def load_formatter(self): + opener = StreamOpener(stream=sys.stdout) + allure_formatter = AllureFormatter(opener, self.config) + pretty_formatter = PrettyFormatter(opener, self.config) + self.formatters.append(allure_formatter) + self.formatters.append(pretty_formatter) + + def run(self): + self.context = Context(self) + self.load_hooks() + self.load_step_definitions() + self.load_features() + self.load_formatter() + self.run_model() + class AllureBehaveRunner: def __init__(self, pytester: Pytester, request: FixtureRequest): @@ -42,46 +111,141 @@ def __init__(self, pytester: Pytester, request: FixtureRequest): self.exit_code = None self.allure_results = None - def run_allure_behave(self, *args: str): - result = self.runner.run_allure_integration(( - "--no-snippets", - "-f", "allure_behave.formatter:AllureFormatter", - "-o", "allure-results", - *args - )) - self.exit_code = result["return-value"] - self.allure_results = result["allure-results"] - - def run_feature_by_docstring_path(self): - self.run_allure_behave( - get_path_from_docstring(self.request) + def run_behave( + self, + *, + features: Sequence[str] = None, + steps: Sequence[str] = None, + environment: str = None, + feature_paths: Sequence[str] = None, + step_paths: Sequence[str] = None, + environment_path: str = None, + ) -> None: + """Runs behave against specific set of features, steps and an + environment each specified either as a string literal or as a path to a + file. + + Arguments: + features (Sequence[str]): a sequence of strings each representing + a .feature file content. + steps (Sequence[str]): a sequence of strings each representing + content of a step definition file. + environment (str): a string representing content of the + environment.py file. + feature_paths (Sequence[str]): a sequence of feature files. + step_paths (Sequence[str]): a sequence of step definition files. + environment_path (str): a path to the environment.py file. + + The results of the run could be accessed through the + :code:`allure_results` attribute. + """ + + path_to_fake = "allure_behave.formatter.AllureFileLogger" + testdir = self.request.path.parent + features = self.__extend_content_seq(testdir, features, feature_paths) + steps = self.__extend_content_seq(testdir, steps, step_paths) + environment = self.__resolve_content( + testdir, + environment, + environment_path + ) + with fake_logger(path_to_fake) as allure_results: + InMemoryBehaveRunner(features, steps, environment).run() + self.allure_results = allure_results + + def run_rst_example( + self, + *features: str, + steps: Sequence[str] = None, + feature_literals: Sequence[str] = None, + step_literals: Sequence[str] = None, + environment: str = None, + environment_literal: str = None + ) -> None: + """Loads code blocks from reStructuredText document and executes behave + against them. + + Arguments: + *features (str): names of the feature code blocks + + Keyword arguments: + feature_literals (Sequence[str]): a sequence of strings, each + representing a .feature file content. + steps (Sequence[str]): names of the code blocks defining steps. + step_literals (Sequence[str]): a sequence of strings, each + representing a step definition file content. + environment (str): an optional name of the environment.py code + block. + environment_literal (str): an optional string, representing the + environment.py file content. + + Note: + See :class:`RstExampleTable` for more details. + + Note: + Uses in-memory execution model. See :meth:`run_behave_in_memory` for + more details. + + The results of the run could be accessed through the + :code:`allure_results` attribute. + """ + + examples_table = RstExampleTable.find_examples(self.request) + self.run_behave( + features=self.__resolve_code_blocks( + examples_table, + features, + feature_literals + ), + steps=self.__resolve_code_blocks( + examples_table, + steps, + step_literals + ), + environment=examples_table[environment] if environment else\ + environment_literal + ) + + @staticmethod + def __extend_content_seq(testdir, content_seq, paths): + if content_seq is None: + content_seq = [] + if paths is None: + paths = [] + return chain( + content_seq, + (AllureBehaveRunner.__read_test_file(testdir, p) for p in paths) + ) + + @staticmethod + def __read_test_file(testdir, path): + fullpath = testdir.joinpath(path) + with open(fullpath, encoding="utf-8") as f: + return f.read() + + @staticmethod + def __resolve_content(testdir, content, path): + if content is None and path is not None: + content = AllureBehaveRunner.__read_test_file(testdir, path) + return content + + @staticmethod + def __pick_examples(table, example_names): return ( + table[n] for n in example_names + ) + + @staticmethod + def __resolve_code_blocks(table, code_block_names, literals): + code_block_names = code_block_names or [] + literals = literals or [] + return chain( + AllureBehaveRunner.__pick_examples(table, code_block_names), + literals ) - def run_feature_of_current_test(self, **fmt_kwargs): - test_folder = self.request.node.path.parent - self.__generate_test_features(test_folder, fmt_kwargs) - self.__copy_test_feature_steps(test_folder) - self.run_allure_behave(self.pytester.path) - - def __generate_test_features(self, test_folder, fmt_kwargs): - for feature_path in test_folder.glob("*.feature"): - dst_path = self.pytester.path.joinpath(feature_path.name) - with open(feature_path, "r", encoding="utf-8") as src: - with open(dst_path, "w+", encoding="utf-8") as dst: - dst.writelines( - line.format_map(fmt_kwargs) for line in src.readlines() - ) - - def __copy_test_feature_steps(self, test_folder): - tmp_steps_folder = self.pytester.path.joinpath("steps") - tmp_steps_folder.mkdir(exist_ok=True) - for step_path in test_folder.glob("*_steps.py"): - if not step_path.name.startswith("test_"): - dst_path = tmp_steps_folder.joinpath(step_path.name) - shutil.copyfile(step_path, dst_path) @fixture -def allure_behave_runner(pytester: Pytester, request: FixtureRequest): +def behave_runner(pytester: Pytester, request: FixtureRequest): return AllureBehaveRunner(pytester, request) @@ -90,4 +254,4 @@ def executed_docstring_path(allure_behave_runner): allure_behave_runner.run_feature_by_docstring_path() return allure_behave_runner -__fix_behave_multirun() \ No newline at end of file +__fix_behave_in_memory_run() \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index d984e3f5..da833df0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -2,9 +2,16 @@ import mock import shutil import runpy +import warnings +import docutils.nodes +import docutils.parsers.rst +import docutils.utils +import docutils.frontend +from pathlib import Path from doctest import script_from_examples from contextlib import contextmanager -from typing import Sequence +from typing import Mapping, Tuple, TypeVar +from pytest import FixtureRequest, Pytester import allure_commons from allure_commons_test.report import AllureReport @@ -20,13 +27,15 @@ def pytest_configure(config: pytest.Config): @contextmanager -def fake_logger(): +def fake_logger(path :str = None) -> None: + if path is None: + path = "allure_commons.logger.AllureFileLogger" blocked_plugins = [] for name, plugin in allure_commons.plugin_manager.list_name_plugin(): allure_commons.plugin_manager.unregister(plugin=plugin, name=name) blocked_plugins.append(plugin) - with mock.patch("allure_commons.logger.AllureFileLogger") as ReporterMock: + with mock.patch(path) as ReporterMock: ReporterMock.return_value = AllureMemoryLogger() yield ReporterMock.return_value @@ -34,16 +43,151 @@ def fake_logger(): allure_commons.plugin_manager.register(plugin) -def get_path_from_docstring(request: pytest.FixtureRequest): +def get_docstring(node: pytest.Item) -> str: + if isinstance(node, pytest.Function): + return node.function.__doc__ + elif isinstance(node, pytest.Class): + return node.cls.__doc__ + elif isinstance(node, pytest.Module): + return node.module.__doc__ + elif isinstance(node, pytest.Package): + return node.obj.__doc__ + return None + + +def get_path_from_docstring(request: FixtureRequest) -> Path: return request.config.rootpath.joinpath( - ( - request.node.function.__doc__ or - request.node.module.__doc__ - ).strip() + get_docstring(request.node).strip() ) +RstExampleTableT = TypeVar("RstExampleTableT", bound="RstExampleTable") +class RstExampleTable: + + STASH_KEY = pytest.StashKey() + + def __init__(self, filepath: str|Path) -> None: + self.source = filepath + self.examples = self.load_examples(filepath) + + def __getitem__(self, name: str) -> str: + if name not in self.examples: + raise KeyError(f"Code block {name!r} not found in {self.source}") + return self.examples[name] + + @staticmethod + def find_examples(request: FixtureRequest) -> RstExampleTableT: + """Loads code examples, associated with the test node or one of its + parents. + + Arguments: + request (FixtureRequest): the request fixture + + Returns (RstExampleTable): A table containing the examples, indexed by + their names from the .rst document + + Notes: + It first finds a docstring defined on the function, class, module or + package associated with the node. If multiple docstrings exist, + the one defined on a lower level wins. + + The docstring content is used then as the path to the + reStructuredText document. The document is parsed and all its code + blocks are combined in a dictionary mapping their names to the code + blocks themselves. The mapping can be accessed through the returned + instance of RstExampleTable class. + + The table is then cached in a stash of the same node from which the + original docstring was loaded. This prevents from parsing the same + document several times if multiple tests from the same module/package + (or parametrizations of the same metafunc) are testing the examples + from the same document. + """ + + node, docstring = RstExampleTable.find_node_with_docstring_or_throw( + request + ) + examples = RstExampleTable.__get_from_cache(node) + if examples is None: + examples = RstExampleTable.__create_and_cache(node, docstring) + return examples + + @staticmethod + def find_node_with_docstring( + request: FixtureRequest + ) -> Tuple[pytest.Item, str]: + node = request.node + while node: + docstring = get_docstring(node) + if docstring: + break + node = node.parent + return node, docstring + + @staticmethod + def find_node_with_docstring_or_throw( + request: FixtureRequest + ) -> Tuple[pytest.Item, str]: + node, docstring = RstExampleTable.find_node_with_docstring(request) + if node is None: + nodeid = request.node.nodeid + raise ValueError(f"Unable to get docstring for node {nodeid}") + return node, docstring + + @staticmethod + def load_examples(filepath: str|Path) -> Mapping[str, str]: + document = RstExampleTable.parse_rst(filepath) + return { + name: code_block.astext() + for code_block in document.findall( + RstExampleTable.__filter + ) for name in code_block["names"] + } + + @staticmethod + def parse_rst(filepath: str|Path) -> docutils.nodes.document: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + parser = docutils.parsers.rst.Parser() + components = (docutils.parsers.rst.Parser,) + settings = docutils.frontend.OptionParser( + components=components + ).get_default_values() + document = docutils.utils.new_document( + str(filepath), + settings=settings + ) + with open(filepath, encoding="utf-8") as f: + content = f.read() + parser.parse(content, document) + return document + + @staticmethod + def __get_from_cache(node): + if RstExampleTable.STASH_KEY in node.stash: + return node.stash[RstExampleTable.STASH_KEY] + + @staticmethod + def __create_and_cache(node, path): + filepath = node.config.rootpath.joinpath(path.strip()) + if not filepath.exists() or filepath.is_dir(): + raise ValueError( + f"Document, referred by {node.nodeid}, " + f"doesn't exist at {filepath}" + ) + examples = RstExampleTable.load_examples(filepath) + node.stash[RstExampleTable.STASH_KEY] = examples + return examples + + @staticmethod + def __filter(node): + return isinstance( + node, + docutils.nodes.literal_block + ) and "code" in node["classes"] and node["names"] + + class AlluredTestdir: - def __init__(self, pytester: pytest.Pytester, request: pytest.FixtureRequest): + def __init__(self, pytester: Pytester, request: FixtureRequest): self.testdir = pytester self.request = request self.allure_report = None @@ -122,28 +266,43 @@ def run_allure_integration(self, *args, **kwargs): "return-value": return_value } + @pytest.fixture -def allured_testdir(pytester: pytest.Pytester, request: pytest.FixtureRequest): +def allured_testdir( + pytester: Pytester, + request: FixtureRequest +) -> AlluredTestdir: fixture = AlluredTestdir(pytester, request) return fixture @pytest.fixture -def executed_docstring_source(allured_testdir: AlluredTestdir): +def executed_docstring_source( + allured_testdir: AlluredTestdir +) -> AlluredTestdir: allured_testdir.parse_docstring_source() allured_testdir.run_with_allure() return allured_testdir @pytest.fixture -def executed_docstring_path(allured_testdir: AlluredTestdir): +def executed_docstring_path( + allured_testdir: AlluredTestdir +) -> AlluredTestdir: allured_testdir.parse_docstring_path() allured_testdir.run_with_allure() return allured_testdir @pytest.fixture -def executed_docstring_directory(allured_testdir: AlluredTestdir): +def executed_docstring_directory( + allured_testdir: AlluredTestdir +) -> AlluredTestdir: allured_testdir.copy_docstring_dir() allured_testdir.run_with_allure() return allured_testdir + + +@pytest.fixture +def rst_examples(request: FixtureRequest) -> RstExampleTable: + return RstExampleTable.find_examples(request) \ No newline at end of file diff --git a/tests/requirements.txt b/tests/requirements.txt new file mode 100644 index 00000000..87069c06 --- /dev/null +++ b/tests/requirements.txt @@ -0,0 +1,3 @@ +docutils +pytest +hamcrest From a0db3a05276d803c0c1dcaff54c2976c9d43e45f Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Thu, 9 Feb 2023 23:56:26 +0700 Subject: [PATCH 03/18] Reworked behave tests and examples --- allure-behave/examples/attachment.rst | 100 +++++++++ allure-behave/examples/description.rst | 6 +- .../examples/description/description.feature | 15 -- .../examples/description/environment.py | 14 -- .../description/steps/description_steps.py | 12 -- allure-behave/examples/label.rst | 2 +- allure-behave/examples/link.feature | 65 ------ allure-behave/examples/link.rst | 107 ++++++++++ allure-behave/examples/no_skipped.feature | 38 ---- allure-behave/examples/scenario.feature | 58 ------ .../examples/scenario_outline.feature | 126 ------------ allure-behave/examples/severity.feature | 70 ------- allure-behave/examples/severity.rst | 65 ++++++ allure-behave/examples/step.feature | 101 --------- allure-behave/examples/steps/behave_steps.py | 73 ------- allure-behave/examples/steps/dummy_steps.py | 50 ----- allure-behave/examples/steps/report_steps.py | 193 ------------------ allure-behave/examples/tag.feature | 50 ----- allure-behave/examples/tag.rst | 37 ++++ allure-behave/examples/test_plan.feature | 130 ------------ allure-behave/examples/testplan.rst | 187 +++++++++++++++++ allure-behave/examples/unicode.feature | 49 ----- .../examples/scenario-outline/__init__.py | 0 .../examples/simple-scenario/__init__.py | 0 allure-python-commons-test/src/result.py | 40 ++-- allure-python-commons/src/types.py | 2 +- .../allure_api/attachment/attachment_test.py | 82 ++++++++ .../description/description_test.py | 0 .../{ => allure_api}/labels/label_test.py | 2 +- .../acceptance/allure_api/links/link_test.py | 97 +++++++++ .../allure_api/severities/severity_test.py | 74 +++++++ .../acceptance/allure_api/tags/tag_test.py | 46 +++++ .../allure_api/testplan/testplan_test.py | 138 +++++++++++++ .../background/background_steps.py | 0 .../background/background_test.py | 70 ++++--- .../behave_cmd/behave_cmd_test.py | 63 ++++++ .../{ => behave_support}/hooks/hook_test.py | 88 ++++---- .../hooks/test-data/attachment-hook.feature | 0 .../hooks/test-data/attachment-hooks.py | 0 .../hooks/test-data/context-step-hooks.py | 0 .../hooks/test-data/feature-tag-hook.feature | 0 .../hooks/test-data/func-step-hooks.py | 0 .../hooks/test-data/global-hooks.feature | 0 .../hooks/test-data/global-hooks.py | 0 .../hooks/test-data/step-hook.feature | 0 .../hooks/test-data/steps.py | 0 .../hooks/test-data/tag-hook.feature | 0 .../hooks/test-data/tag-hooks.py | 0 .../scenario_outline_test.py | 136 ++++++++++++ .../test-data/outline.feature | 34 +++ .../scenario_outlines/test-data/steps.py | 5 + .../behave_support/scenarios/scenario_test.py | 143 +++++++++++++ .../behave_support/steps/behave_step_test.py | 172 ++++++++++++++++ tests/allure_behave/conftest.py | 45 ++-- tests/conftest.py | 82 +++----- 55 files changed, 1649 insertions(+), 1218 deletions(-) create mode 100644 allure-behave/examples/attachment.rst delete mode 100644 allure-behave/examples/description/description.feature delete mode 100644 allure-behave/examples/description/environment.py delete mode 100644 allure-behave/examples/description/steps/description_steps.py delete mode 100644 allure-behave/examples/link.feature create mode 100644 allure-behave/examples/link.rst delete mode 100644 allure-behave/examples/no_skipped.feature delete mode 100644 allure-behave/examples/scenario.feature delete mode 100644 allure-behave/examples/scenario_outline.feature delete mode 100644 allure-behave/examples/severity.feature create mode 100644 allure-behave/examples/severity.rst delete mode 100644 allure-behave/examples/step.feature delete mode 100644 allure-behave/examples/steps/behave_steps.py delete mode 100644 allure-behave/examples/steps/dummy_steps.py delete mode 100644 allure-behave/examples/steps/report_steps.py delete mode 100644 allure-behave/examples/tag.feature create mode 100644 allure-behave/examples/tag.rst delete mode 100644 allure-behave/examples/test_plan.feature create mode 100644 allure-behave/examples/testplan.rst delete mode 100644 allure-behave/examples/unicode.feature create mode 100644 allure-pytest-bdd/examples/scenario-outline/__init__.py create mode 100644 allure-pytest-bdd/examples/simple-scenario/__init__.py create mode 100644 tests/allure_behave/acceptance/allure_api/attachment/attachment_test.py rename tests/allure_behave/acceptance/{ => allure_api}/description/description_test.py (100%) rename tests/allure_behave/acceptance/{ => allure_api}/labels/label_test.py (94%) create mode 100644 tests/allure_behave/acceptance/allure_api/links/link_test.py create mode 100644 tests/allure_behave/acceptance/allure_api/severities/severity_test.py create mode 100644 tests/allure_behave/acceptance/allure_api/tags/tag_test.py create mode 100644 tests/allure_behave/acceptance/allure_api/testplan/testplan_test.py rename tests/allure_behave/acceptance/{ => behave_support}/background/background_steps.py (100%) rename tests/allure_behave/acceptance/{ => behave_support}/background/background_test.py (50%) create mode 100644 tests/allure_behave/acceptance/behave_support/behave_cmd/behave_cmd_test.py rename tests/allure_behave/acceptance/{ => behave_support}/hooks/hook_test.py (79%) rename tests/allure_behave/acceptance/{ => behave_support}/hooks/test-data/attachment-hook.feature (100%) rename tests/allure_behave/acceptance/{ => behave_support}/hooks/test-data/attachment-hooks.py (100%) rename tests/allure_behave/acceptance/{ => behave_support}/hooks/test-data/context-step-hooks.py (100%) rename tests/allure_behave/acceptance/{ => behave_support}/hooks/test-data/feature-tag-hook.feature (100%) rename tests/allure_behave/acceptance/{ => behave_support}/hooks/test-data/func-step-hooks.py (100%) rename tests/allure_behave/acceptance/{ => behave_support}/hooks/test-data/global-hooks.feature (100%) rename tests/allure_behave/acceptance/{ => behave_support}/hooks/test-data/global-hooks.py (100%) rename tests/allure_behave/acceptance/{ => behave_support}/hooks/test-data/step-hook.feature (100%) rename tests/allure_behave/acceptance/{ => behave_support}/hooks/test-data/steps.py (100%) rename tests/allure_behave/acceptance/{ => behave_support}/hooks/test-data/tag-hook.feature (100%) rename tests/allure_behave/acceptance/{ => behave_support}/hooks/test-data/tag-hooks.py (100%) create mode 100644 tests/allure_behave/acceptance/behave_support/scenario_outlines/scenario_outline_test.py create mode 100644 tests/allure_behave/acceptance/behave_support/scenario_outlines/test-data/outline.feature create mode 100644 tests/allure_behave/acceptance/behave_support/scenario_outlines/test-data/steps.py create mode 100644 tests/allure_behave/acceptance/behave_support/scenarios/scenario_test.py create mode 100644 tests/allure_behave/acceptance/behave_support/steps/behave_step_test.py diff --git a/allure-behave/examples/attachment.rst b/allure-behave/examples/attachment.rst new file mode 100644 index 00000000..ca684659 --- /dev/null +++ b/allure-behave/examples/attachment.rst @@ -0,0 +1,100 @@ +=========== +Attachments +=========== + +You can attach data and files to behave test results. An attachment can be added +from a step definition function or from a hook. See examples below for more +details. + +---------------------------------- +Attach data from a step definition +---------------------------------- + +The easiest way to add an attachment is to call the :code:`allure.attach` +function from a step definition: + +Feature file: +^^^^^^^^^^^^^ +.. code:: gherkin + :name: data-attachment-feature + + Feature: Allure attachments in behave tests + Scenario: Data attachment from step definitions + Given a step that adds a named attachment + And a step that adds a typed named attachment + +Step definition file: +^^^^^^^^^^^^^^^^^^^^^ +.. code:: python + :name: data-attachment-steps + + import allure + from behave import given + + @given("a step that adds a named attachment") + def step_impl(context): + allure.attach( + "This is the attachment with the name 'step.txt'", + name="step.txt" + ) + + @given("a step that adds a typed named attachment") + def step_impl(context): + allure.attach( + ( + "[DEBUG] This attachment is named 'trace.log' and has TEXT " + "document appearance" + ), + name="trace.log", + attachment_type=allure.attachment_type.TEXT + ) + +---------------------------------- +Attach file from a step definition +---------------------------------- + +Call the :code:`allure.attach.file` function to attach a file: + +Feature file: +^^^^^^^^^^^^^ +.. code:: gherkin + :name: file-attachment-feature + + Feature: Allure attachments in behave tests + Scenario: File attachment from a step definition + Given a step that attaches a file + +Step definition file: +^^^^^^^^^^^^^^^^^^^^^ +.. code:: python + :name: file-attachment-steps + + import allure + from behave import given + + @given("a step that attaches a file") + def step_impl(context): + allure.attach.file( + "./logs/web", + name="web.log", + attachment_type=allure.attachment_type.TEXT + ) + +------------------ +Attach from a hook +------------------ + +You can also attach data and files from a behave hook, e.g., from the +:code:`after_scenario`: + +.. code:: python + :name: attach-hook + + import allure + + def after_scenario(context, scenario): + allure.attach( + "This attachment will appear on a scenario level", + name="attachment.txt", + attachment_type=allure.attachment_type.TEXT + ) diff --git a/allure-behave/examples/description.rst b/allure-behave/examples/description.rst index b0a860d8..d0e9088d 100644 --- a/allure-behave/examples/description.rst +++ b/allure-behave/examples/description.rst @@ -1,6 +1,6 @@ -==================================== -Provide a description for a scenario -==================================== +===================================== +Provide a description for a test case +===================================== Scenario description can be added in various ways: diff --git a/allure-behave/examples/description/description.feature b/allure-behave/examples/description/description.feature deleted file mode 100644 index de6be3c7..00000000 --- a/allure-behave/examples/description/description.feature +++ /dev/null @@ -1,15 +0,0 @@ -Feature: Allure description for behave tests - Scenario: Description from a .feature file - This scenario has a description. - This description spans across multiple lines. - - Given step that passes - - Scenario: Description from a step definition function - Given step which adds a dynamic description to the scenario - - Scenario: Description from before_scenario hook - Given step that passes - - Scenario: Description from after_scenario hook - Given step that passes diff --git a/allure-behave/examples/description/environment.py b/allure-behave/examples/description/environment.py deleted file mode 100644 index cfde9767..00000000 --- a/allure-behave/examples/description/environment.py +++ /dev/null @@ -1,14 +0,0 @@ -import allure - -def before_scenario(_, scenario): - if "before_scenario" in scenario.name: - allure.dynamic.description( - "This scenario has a description specified in before_scenario hook" - ) - - -def after_scenario(_, scenario): - if "after_scenario" in scenario.name: - allure.dynamic.description( - "This scenario has a description specified in after_scenario hook" - ) diff --git a/allure-behave/examples/description/steps/description_steps.py b/allure-behave/examples/description/steps/description_steps.py deleted file mode 100644 index b812f783..00000000 --- a/allure-behave/examples/description/steps/description_steps.py +++ /dev/null @@ -1,12 +0,0 @@ -import allure -from behave import given - -@given("step that passes") -def given_step_that_passes(_): - pass - -@given("step which adds a dynamic description to the scenario") -def given_step_which_adds_a_dynamic_description_to_the_scenario(_): - allure.dynamic.description( - "This scenario has a description specified by a step definition" - ) \ No newline at end of file diff --git a/allure-behave/examples/label.rst b/allure-behave/examples/label.rst index 31a3e07a..526ee5ff 100644 --- a/allure-behave/examples/label.rst +++ b/allure-behave/examples/label.rst @@ -1,5 +1,5 @@ ================================= -Add a custom label for a scenario +Add a custom label to a test case ================================= It's possible to add a custom label to a behave scenario. Simply apply diff --git a/allure-behave/examples/link.feature b/allure-behave/examples/link.feature deleted file mode 100644 index 82e490ae..00000000 --- a/allure-behave/examples/link.feature +++ /dev/null @@ -1,65 +0,0 @@ -Feature: Link - - Scenario: Scenario issue link - Given feature definition - """ - Feature: Step status - - @allure.issue:http://qameta.io - Scenario: Scenario with passed step - Given simple passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with passed step" - And scenario has "http://qameta.io" link with type "issue" - - Scenario: Feature user link - Given feature definition - """ - @allure.link.homepage:http://qameta.io - Feature: Step status - - Scenario: Scenario with passed step - Given simple passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with passed step" - And scenario has "http://qameta.io" link - - Scenario: Feature and scenario user link - Given feature definition - """ - @allure.link.homepage:http://qameta.io - Feature: Step status - - @allure.issue:http://example.com - Scenario: Scenario with passed step - Given simple passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with passed step" - And scenario has "http://qameta.io" link - And scenario has "http://example.com" link with type "issue" - - Scenario: Dynamic link - Given feature definition - """ - Feature: Step status - - Scenario: Scenario with passed step - Given simple passed step - """ - And hooks implementation - """ - import allure - import allure_commons - - @allure_commons.fixture - def before_scenario(context, scenario): - allure.dynamic.link("http://qameta.io") - allure.dynamic.issue("http://example.com") - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with passed step" - And scenario has "http://qameta.io" link - And scenario has "http://example.com" link with type "issue" \ No newline at end of file diff --git a/allure-behave/examples/link.rst b/allure-behave/examples/link.rst new file mode 100644 index 00000000..3d653a76 --- /dev/null +++ b/allure-behave/examples/link.rst @@ -0,0 +1,107 @@ +========================= +Add a link to a test case +========================= + +There are two ways of adding a link to a test result: + +#. Using gherkin tags +#. Dynamically in python code + +-------------------------------- +Using gherkin tags to add a link +-------------------------------- + +You can associate a link with a test by applying the +:code:`@allure.link.:` tag to the scenario: + +.. code:: gherkin + :name: link-scenario-feature + + Feature: Allure link support + @allure.link.report:https://qameta.io/allure-report/ + Scenario: Scenario with the link to the allure report website + Given noop + +A link can also be associated with any scenario in a feature by applying the tag +to the feature: + +.. code:: gherkin + :name: link-feature-feature + + @allure.link.homepage:https://qameta.io + Feature: Allure link support + Scenario: Scenario with the link to the homepage + Given noop + + Scenario: Another scenario with the link to the homepage + Given noop + +A link to an issue or a test case can be added with :code:`@allure.issue:` +and :code:`@allure.tms:`: + +.. code:: gherkin + :name: specialized-links-feature + + Feature: Allure link support + @allure.issue:https://github.com/allure-framework/allure-python/issues/1 + Scenario: Scenario associated with the issue + Given noop + + @allure.tms:https://qameta.io/#features + Scenario: Scenario associated with the TMS test case + Given noop + +Please, note, that no whitespaces are allowed in a gherkin tag. Make sure your +URLs are `properly encoded `_. + +---------------------------- +Providing a link dynamically +---------------------------- + +You may also create a link dynamically and associate it with a test case in +various places in your python code. + +Example +^^^^^^^ +Here we add two links dynamically: one from the :code:`before_scenario` hook and +another one from the step definition. + +Feature file: +""""""""""""" + +.. code:: gherkin + :name: dynamic-links-feature + + Feature: Allure link support + Scenario: Issue from the step definition and link from the hook + Given a link is associated with this test case + +Steps definition file: +"""""""""""""""""""""" + +.. code:: python + :name: dynamic-links-steps + + import allure + from behave import given + + @given("a link is associated with this test case") + def step_impl(context): + allure.dynamic.issue( + "https://github.com/allure-framework/allure-python/issues/1", + "Skip None and empty values in json" + ) + +environment.py: +""""""""""""""" + +.. code:: python + :name: dynamic-links-hooks + + import allure + + def before_scenario(context, scenario): + allure.dynamic.link( + "https://qameta.io/allure-report/", + name="Allure Report" + ) diff --git a/allure-behave/examples/no_skipped.feature b/allure-behave/examples/no_skipped.feature deleted file mode 100644 index dffacae9..00000000 --- a/allure-behave/examples/no_skipped.feature +++ /dev/null @@ -1,38 +0,0 @@ -Feature: Behave cmd options - - Scenario: Tags in scenario with options "--tags=tag" - Given feature definition - """ - Feature: Tags filtering - - @tag - Scenario: Scenario with tag - Given simple passed step - - Scenario: Scenario without tag - Given simple passed step - """ - When I run behave with allure formatter with options "--tags=tag" - Then allure report has a scenario with name "Scenario with tag" - And this scenario has "passed" status - - Then allure report has a scenario with name "Scenario without tag" - And this scenario has "skipped" status - - Scenario: Tags in scenario with options "--tags=tag --no-skipped" - Given feature definition - """ - Feature: Tags filtering - - @tag - Scenario: Scenario with tag - Given simple passed step - - Scenario: Scenario without tag - Given simple passed step - """ - When I run behave with allure formatter with options "--tags=tag --no-skipped" - Then allure report has a scenario with name "Scenario with tag" - And this scenario has "passed" status - - Then allure report has not a scenario with name "Scenario without tag" diff --git a/allure-behave/examples/scenario.feature b/allure-behave/examples/scenario.feature deleted file mode 100644 index 6b2f8268..00000000 --- a/allure-behave/examples/scenario.feature +++ /dev/null @@ -1,58 +0,0 @@ -Feature: Scenario - - Scenario Outline: Scenario status - Given feature definition - """ - Feature: Scenario - - Scenario: Scenario with step - Given step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with step" - And this scenario has "" status - And this scenario contains step "Given step" - And this step has "" status - - Examples: statuses - | step type | status | - | passed | passed | - | failed | failed | - | broken | broken | - | undefined | broken | - - - Scenario: Skip steps after fail step - Given feature definition - """ - Feature: Scenario - - Scenario: Scenario with failed step in chain - Given passed step - Given failed step - Given broken step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with failed step in chain" - And this scenario has "failed" status - - And this scenario contains step "Given passed step" - And this step has "passed" status - - And this scenario contains step "Given failed step" - And this step has "failed" status - - And this scenario contains step "Given broken step" - And this step has "skipped" status - - - Scenario: Scenario without name - Given feature definition - """ - Feature: Scenario - - Scenario: - Given passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario" diff --git a/allure-behave/examples/scenario_outline.feature b/allure-behave/examples/scenario_outline.feature deleted file mode 100644 index 60907c9f..00000000 --- a/allure-behave/examples/scenario_outline.feature +++ /dev/null @@ -1,126 +0,0 @@ -Feature: Scenario Outline - - Scenario: Scenario outline with one examples table - Given feature definition - """ - Feature: Scenario Outline - - Scenario Outline: Scenario outline with one examples table - Given simple passed step with param "" - - Examples: examples table - | user | - | Alice | - | Bob | - """ - When I run behave with allure formatter - - Then allure report has a scenario with name "Scenario outline with one examples table -- @1.1 examples table" - And scenario contains step "Given simple passed step with param "Alice"" - And this scenario has parameter "user" with value "Alice" - - Then allure report has a scenario with name "Scenario outline with one examples table -- @1.2 examples table" - And scenario contains step "Given simple passed step with param "Bob"" - And this scenario has parameter "user" with value "Bob" - - Scenario: Scenario outline with many examples table - Given feature definition - """ - Feature: Scenario Outline - - Scenario Outline: Scenario outline with many examples table - Given simple passed step with params " " - - Examples: first table - | parameter one | parameter two | - | Peter | I | - | Catherine | II | - - Examples: second table - | parameter one | parameter two | - | Richard | the Lionheart | - | Alexander | the Great | - """ - When I run behave with allure formatter - - Then allure report has a scenario with name "Scenario outline with many examples table -- @1.1 first table" - And scenario contains step "Given simple passed step with params "Peter I"" - And this scenario has parameter "parameter one" with value "Peter" - And this scenario has parameter "parameter two" with value "I" - - Then allure report has a scenario with name "Scenario outline with many examples table -- @1.2 first table" - And scenario contains step "Given simple passed step with params "Catherine II"" - And this scenario has parameter "parameter one" with value "Catherine" - And this scenario has parameter "parameter two" with value "II" - - Then allure report has a scenario with name "Scenario outline with many examples table -- @2.1 second table" - And scenario contains step "Given simple passed step with params "Richard the Lionheart"" - And this scenario has parameter "parameter one" with value "Richard" - And this scenario has parameter "parameter two" with value "the Lionheart" - - Then allure report has a scenario with name "Scenario outline with many examples table -- @2.2 second table" - And scenario contains step "Given simple passed step with params "Alexander the Great"" - And this scenario has parameter "parameter one" with value "Alexander" - And this scenario has parameter "parameter two" with value "the Great" - - - Scenario: Many scenario outlines with one examples table - Given feature definition - """ - Feature: Scenario Outline - - Scenario Outline: First scenario outline in feature - Given simple passed step with param "" - - Examples: examples table - | friend | - | Rick | - | Morty | - - - Scenario Outline: Second scenario outline in feature - Given simple passed step with param "" - - Examples: examples table - | hero | - | Finn | - | Jack | - """ - When I run behave with allure formatter - - Then allure report has a scenario with name "First scenario outline in feature -- @1.1 examples table" - And scenario contains step "Given simple passed step with param "Rick"" - And this scenario has parameter "friend" with value "Rick" - - Then allure report has a scenario with name "First scenario outline in feature -- @1.2 examples table" - And scenario contains step "Given simple passed step with param "Morty"" - And this scenario has parameter "friend" with value "Morty" - - Then allure report has a scenario with name "Second scenario outline in feature -- @1.1 examples table" - And scenario contains step "Given simple passed step with param "Finn"" - And this scenario has parameter "hero" with value "Finn" - - Then allure report has a scenario with name "Second scenario outline in feature -- @1.2 examples table" - And scenario contains step "Given simple passed step with param "Jack"" - And this scenario has parameter "hero" with value "Jack" - - Scenario Outline: Scenario outline with one examples table - Given feature definition - """ - Feature: Scenario Outline - - Scenario Outline: Scenario outline with example variable in name - Given simple passed step with param "" - - Examples: examples table - | user | - | Alice | - | Bob | - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario outline with example variable in name " - - Examples: examples table - | user | postfix | - | Alice | -- @1.1 examples table | - | Bob | -- @1.2 examples table | \ No newline at end of file diff --git a/allure-behave/examples/severity.feature b/allure-behave/examples/severity.feature deleted file mode 100644 index fcbc289c..00000000 --- a/allure-behave/examples/severity.feature +++ /dev/null @@ -1,70 +0,0 @@ -Feature: Severity - - Scenario: Default severity is normal - Given feature definition - """ - Feature: Severity - - Scenario: Scenario and feature without severity - Given simple passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario and feature without severity" - And scenario has "normal" severity - - - Scenario: Severity tag in feature - Given feature definition - """ - @critical - Feature: Severity - - Scenario: Scenario in feature with critical severity - Given simple passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario in feature with critical severity" - And scenario has "critical" severity - - - Scenario: Severity tag in scenario - Given feature definition - """ - Feature: Severity - - @critical - Scenario: Scenario with critical severity in feature without severity - Given simple passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with critical severity in feature without severity" - And scenario has "critical" severity - - - Scenario: Severity tag in scenario and feature - Given feature definition - """ - @critical - Feature: Severity - - @minor - Scenario: Scenario with minor severity in feature with critical severity - Given simple passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with minor severity in feature with critical severity" - And scenario has "minor" severity - - - Scenario: Repeated severity tag - Given feature definition - """ - Feature: Severity - - @minor @critical @blocker - Scenario: Scenario with repeated severity tag - Given simple passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with repeated severity tag" - And scenario has "blocker" severity \ No newline at end of file diff --git a/allure-behave/examples/severity.rst b/allure-behave/examples/severity.rst new file mode 100644 index 00000000..8a4ec502 --- /dev/null +++ b/allure-behave/examples/severity.rst @@ -0,0 +1,65 @@ +=============================== +Specify severity of a test case +=============================== + +Use one of the following tags to specify severity of a test case: + +#. :code:`@trivial` +#. :code:`@minor` +#. :code:`@normal` +#. :code:`@critical` +#. :code:`@blocker` + +Those tags can be applied to scenarios: + +.. code:: gherkin + :name: severity-on-scenario + + Feature: Allure severity support + @blocker + Scenario: Blocking scenario + Given noop + + @critical + Scenario: Critical scenario + Given noop + + @normal + Scenario: Normal scenario + Given noop + + @minor + Scenario: Minor scenario + Given noop + + @trivial + Scenario: Trivial scenario + Given noop + +A severity tag can also be applied to a feature, affecting all scenarios of the +feature: + +.. code:: gherkin + :name: severity-on-feature + + @critical + Feature: Allure severity support + Scenario: This scenario inherits the @cricial tag + Given noop + + Scenario: This scenario also inherits the @cricial tag + Given noop + +If multiple severity tags are affecting a scenario, the last one wins: + +.. code:: gherkin + :name: multiple-severities + + @critical, @blocker + Feature: Allure severity support + @minor @trivial + Scenario: This is a trivial scenario + Given noop + + Scenario: While this one is a blocker + Given noop diff --git a/allure-behave/examples/step.feature b/allure-behave/examples/step.feature deleted file mode 100644 index 7b434b5d..00000000 --- a/allure-behave/examples/step.feature +++ /dev/null @@ -1,101 +0,0 @@ -Feature: Step - - Scenario: Failed step - Given feature definition - """ - Feature: Step status - - Scenario: Scenario with failed step - Given simple failed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with failed step" - And scenario contains step "Given simple failed step" - And this step has "failed" status - And this step has status details with message "AssertionError: Assert message" - - - Scenario: Broken step - Given feature definition - """ - Feature: Step status - - Scenario: Scenario with broken step - Given simple broken step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with broken step" - And scenario contains step "Given simple broken step" - And this step has "broken" status - And this step has status details with message "ZeroDivisionError" - - - Scenario: Step text parameter - Given feature definition - """ - Feature: Step Data - - Scenario: Scenario with step contains text data - Given simple passed step with text data - ''' - Some text in step - ''' - And simple passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with step contains text data" - And scenario contains step "Given simple passed step with text data" - And this step has attachment - And this step has "passed" status - - - Scenario: Step table parameter - Given feature definition - """ - Feature: Step Data - - Scenario: Scenario with step contains table data - Given simple passed step with table data - | name | value | - | e | 2 | - | e | 4 | - - And simple passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with step contains table data" - And scenario contains step "Given simple passed step with table data" - And this step has attachment - And this step has "passed" status - - - Scenario: Step attachment - Given feature definition - """ - Feature: Step Data - - Scenario: Scenario with step that attached data - Given passed step with attachment - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with step that attached data" - And scenario contains step "Given passed step with attachment" - And this step has attachment - And this step has "passed" status - - Scenario: Step with table data containing comma - Given feature definition - """ - Feature: Step with data - - Scenario: Step with a table data containing comma - Given step with a table data - |Items A|Items B| - |Item 1, Item 2|Item 3, Item 4| - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Step with a table data containing comma" - And scenario contains step "Given step with a table data" - And this step has attachment ".table" with the following data - |Items A|Items B| - |Item 1, Item 2|Item 3, Item 4| \ No newline at end of file diff --git a/allure-behave/examples/steps/behave_steps.py b/allure-behave/examples/steps/behave_steps.py deleted file mode 100644 index b90f54dc..00000000 --- a/allure-behave/examples/steps/behave_steps.py +++ /dev/null @@ -1,73 +0,0 @@ -import os -from tempfile import mkdtemp -import allure_commons -from allure_commons_test.report import AllureReport -from behave.parser import Parser -from behave.runner import ModelRunner -from behave.configuration import Configuration -from behave.formatter._registry import make_formatters -from behave.formatter.base import StreamOpener -import tempfile -from contextlib import contextmanager - - -@given('feature definition') -@given('feature definition {lang}') -def feature_definition(context, **kwargs): - parser = Parser(language=kwargs.get('lang', None)) - feature = parser.parse(context.text) - if hasattr(context, "feature_definition"): - context.feature_definition.append(feature) - else: - context.feature_definition = [feature] - - -@given('hooks implementation') -def hooks_implementations(context): - context.globals = {} - exec(context.text, context.globals) - - -@given('test plan') -def test_plan_helper(context): - tmp_dir = os.environ.get("TEST_TMP") - file, filename = tempfile.mkstemp(suffix=".json", dir=tmp_dir) - os.environ["ALLURE_TESTPLAN_PATH"] = filename - with os.fdopen(file, 'w') as tmp: - tmp.write(context.text) - context.test_plan = filename - - -@when('I run behave with allure formatter') -@when('I run behave with allure formatter with options "{args}"') -def run_behave_with_allure(context, **kwargs): - with test_context(): - cmd_args = '-f allure_behave.formatter:AllureFormatter' - cmd_options = kwargs.get('args', '') - cmd = f'{cmd_options} {cmd_args}' - config = Configuration(command_args=cmd) - result_tmp_dir = mkdtemp(dir=os.environ.get('TEST_TMP', None)) - stream_opener = StreamOpener(filename=result_tmp_dir) - model_runner = ModelRunner(config, context.feature_definition) - model_runner.formatters = make_formatters(config, [stream_opener]) - model_runner.hooks = getattr(context, 'globals', dict()) - model_runner.run() - context.allure_report = AllureReport(result_tmp_dir) - - os.environ.pop("ALLURE_TESTPLAN_PATH", None) - - -@contextmanager -def test_context(): - def _unregister_plugins(): - plugins = [] - for name, plugin in allure_commons.plugin_manager.list_name_plugin(): - allure_commons.plugin_manager.unregister(plugin=plugin, name=name) - plugins.append(plugin) - return plugins - - plugins = _unregister_plugins() - yield - _unregister_plugins() - for plugin in plugins: - allure_commons.plugin_manager.register(plugin) diff --git a/allure-behave/examples/steps/dummy_steps.py b/allure-behave/examples/steps/dummy_steps.py deleted file mode 100644 index de48c0eb..00000000 --- a/allure-behave/examples/steps/dummy_steps.py +++ /dev/null @@ -1,50 +0,0 @@ -import allure -from behave import given - - -@given('passed step') -@given("step that passes") -@given('{what} passed step') -@given('passed step {where}') -@given('{what} passed step {where}') -def step_impl(*args, **kwargs): - if 'with attachment' in kwargs.values(): - allure.attach('Hi there!', name='user attachment', attachment_type=allure.attachment_type.TEXT) - pass - - -@given('failed step') -@given('{what} failed step') -@given('failed step {where}') -@given('{what} failed step {where}') -def step_impl(*args, **kwargs): - assert False, 'Assert message' - - -@given('провальный шаг') -def step_impl(*args, **kwargs): - assert False, 'Фиаско!' - - -@given('провальный шаг с ascii') -def step_impl(*args, **kwargs): - assert False, 'Фиаско!' - - -@given('проходящий шаг') -def step_impl(*args, **kwargs): - pass - - -@given('broken step') -@given('{what} broken step') -@given('broken step {where}') -@given('{what} broken step {where}') -def step_impl(*args, **kwargs): - raise ZeroDivisionError() - - -@given('всегда будет <это>') -@given('всегда буду я') -def step_impl(*args, **kwargs): - pass diff --git a/allure-behave/examples/steps/report_steps.py b/allure-behave/examples/steps/report_steps.py deleted file mode 100644 index 0e82fe65..00000000 --- a/allure-behave/examples/steps/report_steps.py +++ /dev/null @@ -1,193 +0,0 @@ -from functools import partial -from hamcrest import assert_that, contains_string -from hamcrest import not_ -from hamcrest import equal_to -from allure_commons.types import AttachmentType -from allure_commons_test.report import has_test_case -from allure_commons_test.result import with_status -from allure_commons_test.result import has_step -from allure_commons_test.result import has_attachment -from allure_commons_test.result import has_attachment_with_content -from allure_commons_test.result import has_parameter -from allure_commons_test.result import has_status_details -from allure_commons_test.result import with_message_contains -from allure_commons_test.result import has_link -from allure_commons_test.result import has_description -from allure_commons_test.container import has_container -from allure_commons_test.container import has_before, has_after -from allure_commons_test.content import csv_equivalent -from allure_commons_test.label import has_severity -from allure_commons_test.label import has_tag -from allure_commons_test.label import has_label - - -def match(matcher, *args): - for i, arg in enumerate(args): - if not hasattr(arg, '__call__'): - matcher = partial(matcher, arg) - else: - matcher = partial(matcher, match(arg, *args[i+1:])) - break - return matcher() - - -@then('allure report has a scenario with name "{scenario}"') -def step_scenario(context, scenario): - matcher = partial(match, has_test_case, scenario) - context.scenario = matcher - assert_that(context.allure_report, matcher()) - - -@then('allure report has not a scenario with name "{scenario}"') -def step_scenario(context, scenario): - matcher = partial(match, not_, has_test_case, scenario) - context.scenario = matcher - assert_that(context.allure_report, matcher()) - - -@then('scenario has before fixture "{fixture}"') -@then('this scenario has before fixture "{fixture}"') -def step_before_fixture(context, fixture): - context_matcher = context.scenario - matcher = partial(context_matcher, has_container, context.allure_report, has_before, fixture) - context.before = matcher - assert_that(context.allure_report, matcher()) - - -@then('scenario has after fixture "{fixture}"') -@then('this scenario has after fixture "{fixture}"') -def step_after_fixture(context, fixture): - context_matcher = context.scenario - matcher = partial(context_matcher, has_container, context.allure_report, has_after, fixture) - context.after = matcher - assert_that(context.allure_report, matcher()) - - -@then('scenario has not before fixture "{fixture}"') -@then('this scenario has not before fixture "{fixture}"') -def step_no_before_fixture(context, fixture): - context_matcher = context.scenario - matcher = partial(context_matcher, not_, has_container, context.allure_report, has_before, fixture) - assert_that(context.allure_report, matcher()) - - -@then('scenario has not after fixture "{fixture}"') -@then('this scenario has not after fixture "{fixture}"') -def step_impl(context, fixture): - context_matcher = context.scenario - matcher = partial(context_matcher, not_, has_container, context.allure_report, has_after, fixture) - assert_that(context.allure_report, matcher()) - - -@then('{item} contains step "{step}"') -@then('this {item} contains step "{step}"') -def step_step(context, item, step): - context_matcher = getattr(context, item) - matcher = partial(context_matcher, has_step, step) - context.step = matcher - assert_that(context.allure_report, matcher()) - - -@then('{item} has "{status}" status') -@then('this {item} has "{status}" status') -def step_status(context, item, status): - context_matcher = getattr(context, item) - matcher = partial(context_matcher, with_status, status) - assert_that(context.allure_report, matcher()) - - -@then('{item} has status details with message "{message}"') -@then('this {item} has status details with message "{message}"') -def step_status(context, item, message): - context_matcher = getattr(context, item) - matcher = partial(context_matcher, has_status_details, with_message_contains, message) - assert_that(context.allure_report, matcher()) - - -@then('scenario has "{severity}" severity') -@then('this scenario has "{severity}" severity') -def step_severity(context, severity): - context_matcher = context.scenario - matcher = partial(context_matcher, has_severity, severity) - assert_that(context.allure_report, matcher()) - - -@then('scenario has "{tag}" tag') -@then('this scenario has "{tag}" tag') -def step_tag(context, tag): - context_matcher = context.scenario - matcher = partial(context_matcher, has_tag, tag) - assert_that(context.allure_report, matcher()) - - -@then('scenario has "{url}" link') -@then('this scenario has "{url}" link') -@then('scenario has "{url}" link with type "{link_type}"') -@then('this scenario has "{url}" link with type "{link_type}"') -@then('scenario has "{url}" link with type "{link_type}" and name "{name}"') -@then('this scenario has "{url}" link with type "{link_type}" and name "{name}"') -def step_link(context, url, link_type=None, name=None,): - context_matcher = context.scenario - matcher = partial(context_matcher, has_link, url, link_type, name) - assert_that(context.allure_report, matcher()) - - -@then('scenario has "{name}" label with value "{value}"') -@then('this scenario has "{name}" label with value "{value}"') -def step_label(context, name, value): - context_matcher = context.scenario - matcher = partial(context_matcher, has_label, name, value) - assert_that(context.allure_report, matcher()) - - -@then('{item} has parameter "{name}" with value "{value}"') -@then('this {item} has parameter "{name}" with value "{value}"') -def step_parameter(context, item, name, value): - context_matcher = getattr(context, item) - matcher = partial(context_matcher, has_parameter, name, value) - assert_that(context.allure_report, matcher()) - - -@then('{item} has attachment') -@then('this {item} has attachment') -def step_attachment(context, item): - context_matcher = getattr(context, item) - matcher = partial(context_matcher, has_attachment) - assert_that(context.allure_report, matcher()) - - -@then('this {item} has attachment "{name}" with the following data') -def step_attachment_data(context, item, name): - context_matcher = getattr(context, item) - attachment_type, content_matcher = _get_attachment_type_and_matcher(context) - matcher = partial( - context_matcher, - partial( - has_attachment_with_content, - context.allure_report.attachments, - content_matcher, - attachment_type.mime_type, - name - ) - ) - assert_that(context.allure_report, matcher()) - - -@then('scenario has description "{description}"') -def step_description(context, description): - context_matcher = context.scenario - matcher = partial(context_matcher, has_description, contains_string(description)) - assert_that(context.allure_report, matcher()) - -def _get_attachment_type_and_matcher(context): - return ( - AttachmentType.CSV, - csv_equivalent( - [context.table.headings] + [ - r.cells for r in context.table.rows - ] - ) - ) if context.table is not None else ( - AttachmentType.TEXT, - equal_to(context.text) - ) \ No newline at end of file diff --git a/allure-behave/examples/tag.feature b/allure-behave/examples/tag.feature deleted file mode 100644 index 5e1d8002..00000000 --- a/allure-behave/examples/tag.feature +++ /dev/null @@ -1,50 +0,0 @@ -Feature: Tags - - Scenario: Tags in feature - Given feature definition - """ - @uno @due @tre - Feature: Tags - - Scenario: Scenario in feature with tags - Given simple passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario in feature with tags" - And scenario has "uno" tag - And scenario has "due" tag - And scenario has "tre" tag - - - Scenario: Tags in scenario - Given feature definition - """ - Feature: Tags - - @uno @due @tre - Scenario: Scenario with tags - Given simple passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with tags" - And scenario has "uno" tag - And scenario has "due" tag - And scenario has "tre" tag - - - Scenario: Tags in feature and scenario - Given feature definition - """ - @uno @due @tre - Feature: Tags - - @tre @quattro - Scenario: Scenario and feature with tags - Given simple passed step - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario and feature with tags" - And scenario has "uno" tag - And scenario has "due" tag - And scenario has "tre" tag - And scenario has "quattro" tag diff --git a/allure-behave/examples/tag.rst b/allure-behave/examples/tag.rst new file mode 100644 index 00000000..e010e4c2 --- /dev/null +++ b/allure-behave/examples/tag.rst @@ -0,0 +1,37 @@ +=============== +Tag a test case +=============== + +You can add an allure tag to a test case by to apply a gherkin tag to a +corresponding scenario: + +.. code:: gherkin + :name: tag-scenario-feature + + Feature: Allure tag support + @distributed + Scenario: Applying a tag directly to a scenario + Given noop + +The tag can also be applied to a feature. In that case it propagates to all +scenarios of the feature: + +.. code:: gherkin + :name: tag-feature-feature + + @isolated + Feature: Allure tag support + Scenario: Applying a tag to a feature + Given noop + +You can add any number of tags that way: + +.. code:: gherkin + :name: tag-multiple-feature + + @node-1 @node-2 + Feature: Allure tag support + @node-3 @node-4 + Scenario: Applying multiple tags + Given noop + diff --git a/allure-behave/examples/test_plan.feature b/allure-behave/examples/test_plan.feature deleted file mode 100644 index 91063178..00000000 --- a/allure-behave/examples/test_plan.feature +++ /dev/null @@ -1,130 +0,0 @@ -Feature: Test plan - Scenario: Select scenarios by fullname - Given feature definition - """ - Feature: Test plan example - - Scenario: Scenario with passed step - Given passed step - - Scenario: Ignored scenario - Given passed step - """ - Given feature definition - """ - Feature: Another Test plan example - - Scenario: Another scenario with passed step - Given passed step - - Scenario: Another ignored scenario - Given passed step - """ - Given test plan - """ - { - "version":"1.0", - "tests": [ - { - "selector": "Test plan example: Scenario with passed step" - }, - { - "selector": "Another Test plan example: Another scenario with passed step" - } - ] - } - """ - When I run behave with allure formatter - Then allure report has a scenario with name "Scenario with passed step" - Then allure report has a scenario with name "Ignored scenario" - And this scenario has "skipped" status - Then allure report has a scenario with name "Another scenario with passed step" - Then allure report has a scenario with name "Another ignored scenario" - And this scenario has "skipped" status - - Scenario: Drop unselected test from report - Given feature definition - """ - Feature: Test plan example - - Scenario: Scenario with passed step - Given passed step - - Scenario: Ignored scenario - Given passed step - """ - Given feature definition - """ - Feature: Another Test plan example - - Scenario: Another scenario with passed step - Given passed step - - Scenario: Another ignored scenario - Given passed step - """ - Given test plan - """ - { - "version":"1.0", - "tests": [ - { - "selector": "Test plan example: Scenario with passed step" - }, - { - "selector": "Another Test plan example: Another scenario with passed step" - } - ] - } - """ - When I run behave with allure formatter with options "-D AllureFormatter.hide_excluded=True" - Then allure report has a scenario with name "Scenario with passed step" - Then allure report has not a scenario with name "Ignored scenario" - Then allure report has a scenario with name "Another scenario with passed step" - Then allure report has not a scenario with name "Another ignored scenario" - - - Scenario: Select scenarios by allureid - Given feature definition - """ - Feature: Test plan example - - @allure.as_id:1 - Scenario: Scenario with passed step - Given passed step - - @allure.as_id:2 - Scenario: Ignored scenario - Given passed step - """ - Given feature definition - """ - Feature: Another Test plan example - - @allure.as_id:3 - Scenario: Another scenario with passed step - Given passed step - - @allure.as_id:4 - Scenario: Another ignored scenario - Given passed step - """ - Given test plan - """ - { - "version":"1.0", - "tests": [ - { - "id": "1" - }, - { - "id": "3" - } - ] - } - """ - When I run behave with allure formatter with options "-D AllureFormatter.hide_excluded=True" - Then allure report has a scenario with name "Scenario with passed step" - Then allure report has not a scenario with name "Ignored scenario" - Then allure report has a scenario with name "Another scenario with passed step" - Then allure report has not a scenario with name "Another ignored scenario" \ No newline at end of file diff --git a/allure-behave/examples/testplan.rst b/allure-behave/examples/testplan.rst new file mode 100644 index 00000000..5b7a9391 --- /dev/null +++ b/allure-behave/examples/testplan.rst @@ -0,0 +1,187 @@ +=========================================== +Use allure testplan to selectivly run tests +=========================================== + +You can filter test cases with a testplan file. Just create a JSON file and put +its path in the :code:`ALLURE_TESTPLAN_PATH` environment variable. + +Then, when you run behave, allure will detect a testplan. Only tests that are +explicitly listed in the plan will be executed. + +To demontrate how a testplan works with behave, lets assume the following +directory structure:: + + . + ├── features + │ ├── steps + │ │ └── steps.py + │ ├── testplan-1.feature + │ └── testplan-2.feature + └── testplan.json + +**testplan-1.feature**: + +.. code:: gherkin + :name: fullname-feature-1 + + Feature: Allure testplan support + Scenario: Scenario selection + Given noop + + Scenario: Scenario deselection + Given noop + +**testplan-2.feature**: + +.. code:: gherkin + :name: fullname-feature-2 + + Feature: Allure testplan support 2 + Scenario: Scenario selection 2 + Given noop + + Scenario: Scenario deselection 2 + Given noop + +**steps.py**: + +.. code:: python + :name: steps + + from behave import given + + @given("noop") + def step_impl(context): + pass + +**testplan.json**: + +.. code:: json + :name: fullname-testplan + + { + "version":"1.0", + "tests": [ + { + "selector": "Allure testplan support: Scenario selection" + }, + { + "selector": "Allure testplan support 2: Scenario selection 2" + } + ] + } + +Now, when we run behave with allure and testplan enabled: + +**on Linux or MacOS, with bash**: + +.. code:: bash + + ALLURE_TESTPLAN_PATH=./testplan.json behave -f allure_behave.formatter:AllureFormatter -f pretty -o allure-results + +or: + +.. code:: bash + + export ALLURE_TESTPLAN_PATH=./testplan.json + behave -f allure_behave.formatter:AllureFormatter -f pretty -o allure-results + +**on Windows, with PowerShell**: + +.. code:: powershell + + $Env:ALLURE_TESTPLAN_PATH = "./testplan.json" + behave -f allure_behave.formatter:AllureFormatter -f pretty -o allure-results + +We can see, that only test cases, enumerated in the testplan, are executed:: + + Feature: Allure testplan support # features/testplan-1.feature:1 + + Scenario: Scenario selection # features/testplan-1.feature:2 + Given noop # features/steps/steps.py:3 0.000s + + Scenario: Scenario deselection # features/testplan-1.feature:5 + Given noop # None + + SKIP Scenario Scenario deselection 2: Not in allure test plan + Feature: Allure testplan support 2 # features/testplan-2.feature:1 + + Scenario: Scenario selection 2 # features/testplan-2.feature:2 + Given noop # features/steps/steps.py:3 0.000s + + Scenario: Scenario deselection 2 # features/testplan-2.feature:5 + Given noop # None + + 2 features passed, 0 failed, 0 skipped + 2 scenarios passed, 0 failed, 2 skipped + 2 steps passed, 0 failed, 2 skipped, 0 undefined + Took 0m0.000s + +------------------------------ +Select test cases by allure id +------------------------------ + +If you link you scenarios to corresponding test cases with the :code:`as_id` +label, you can specify this ID instead to filter tests in the testplan: + +**testplan-1.feature**: + +.. code:: gherkin + :name: id-feature-1 + + Feature: Allure testplan support + @allure.label.as_id:1004 + Scenario: Scenario selection + Given noop + + @allure.label.as_id:1005 + Scenario: Scenario deselection + Given noop + +**testplan-2.feature**: + +.. code:: gherkin + :name: id-feature-2 + + Feature: Allure testplan support 2 + @allure.label.as_id:1006 + Scenario: Scenario selection 2 + Given noop + + @allure.label.as_id:1007 + Scenario: Scenario deselection 2 + Given noop + +**steps.py**: + +.. code:: json + :name: id-testplan + + { + "version":"1.0", + "tests": [ + {"id": "1004"}, + {"id": "1006"} + ] + } + +If we run behave with this testplan, the same set of scenarios will be executed. + +.. Note:: + + You can read more about allure labels support in behave + `here `_. + +--------------------- +Hiding excluded tests +--------------------- + +To hide tests, excluded by the testplan, add the +:code:`-D AllureFormatter.hide_excluded=True` argument: + +.. code:: shell + + behave -f allure_behave.formatter:AllureFormatter -D AllureFormatter.hide_excluded=True -f pretty -o allure-results + +Skipped tests will still be visible in the behave output, but they will not be +included in the allure report. diff --git a/allure-behave/examples/unicode.feature b/allure-behave/examples/unicode.feature deleted file mode 100644 index 67a42fec..00000000 --- a/allure-behave/examples/unicode.feature +++ /dev/null @@ -1,49 +0,0 @@ -Feature: Language - Scenario: Use russian - Given feature definition ru - """ - Свойство: Юникод - - Структура сценария: Солнечный круг, небо вокруг - Пусть всегда будет <это> - Пусть всегда буду я - Примеры: - | это | - | солнце | - | небо | - | мама | - """ - When I run behave with allure formatter with options "--lang ru" - Then allure report has a scenario with name "Солнечный круг, небо вокруг" - And scenario contains step "Пусть всегда будет солнце" - And scenario contains step "Пусть всегда будет небо" - And scenario contains step "Пусть всегда будет мама" - And scenario contains step "Пусть всегда буду я" - - - Scenario: Assert message in step - Given feature definition ru - """ - Свойство: Юникод - - Сценарий: Ошибка с utf-8 сообщением - Допустим провальный шаг - """ - When I run behave with allure formatter with options "--lang ru" - Then allure report has a scenario with name "Ошибка с utf-8 сообщением" - And scenario contains step "Допустим провальный шаг" - And step has status details with message "AssertionError: Фиаско!" - - - Scenario: ASCII assert message in step - Given feature definition ru - """ - Свойство: Юникод - - Сценарий: Ошибка с utf-8 сообщением - Допустим провальный шаг с ascii - """ - When I run behave with allure formatter with options "--lang ru" - Then allure report has a scenario with name "Ошибка с utf-8 сообщением" - And scenario contains step "Допустим провальный шаг с ascii" - And step has status details with message "AssertionError: Фиаско!" diff --git a/allure-pytest-bdd/examples/scenario-outline/__init__.py b/allure-pytest-bdd/examples/scenario-outline/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/allure-pytest-bdd/examples/simple-scenario/__init__.py b/allure-pytest-bdd/examples/simple-scenario/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/allure-python-commons-test/src/result.py b/allure-python-commons-test/src/result.py index 92df5542..3cd20591 100644 --- a/allure-python-commons-test/src/result.py +++ b/allure-python-commons-test/src/result.py @@ -94,14 +94,16 @@ def has_step(name, *matchers): def has_parameter(name, value, *matchers): - return has_entry('parameters', - has_item( - all_of( - has_entry('name', equal_to(name)), - has_entry('value', equal_to(value)), - *matchers - ) - )) + return has_entry( + 'parameters', + has_item( + all_of( + has_entry('name', equal_to(name)), + has_entry('value', equal_to(value)), + *matchers + ) + ) + ) def doesnt_have_parameter(name): @@ -114,13 +116,19 @@ def doesnt_have_parameter(name): def has_link(url, link_type=None, name=None): - return has_entry('links', - has_item( - all_of( - *[has_entry(key, value) for key, value in - zip(('url', 'type', 'name'), (url, link_type, name)) if value is not None] - ) - )) + return has_entry( + 'links', + has_item( + all_of( + *[ + has_entry(key, value) for key, value in zip( + ('url', 'type', 'name'), + (url, link_type, name) + ) if value is not None + ] + ) + ) + ) def has_issue_link(url, name=None): @@ -128,7 +136,7 @@ def has_issue_link(url, name=None): def has_test_case_link(url, name=None): - return has_link(url, link_type='test_case', name=name) + return has_link(url, link_type='tms', name=name) def has_attachment(attach_type=None, name=None): diff --git a/allure-python-commons/src/types.py b/allure-python-commons/src/types.py index 395ac70b..06b77dfa 100644 --- a/allure-python-commons/src/types.py +++ b/allure-python-commons/src/types.py @@ -14,7 +14,7 @@ class Severity(str, Enum): class LinkType: LINK = 'link' ISSUE = 'issue' - TEST_CASE = 'test_case' + TEST_CASE = 'tms' class LabelType(str): diff --git a/tests/allure_behave/acceptance/allure_api/attachment/attachment_test.py b/tests/allure_behave/acceptance/allure_api/attachment/attachment_test.py new file mode 100644 index 00000000..e3e082af --- /dev/null +++ b/tests/allure_behave/acceptance/allure_api/attachment/attachment_test.py @@ -0,0 +1,82 @@ +""" ./allure-behave/examples/attachment.rst """ + +from tests.allure_behave.conftest import AllureBehaveRunner +from hamcrest import assert_that, equal_to +from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_attachment_with_content +from allure_commons_test.result import has_attachment +from allure_commons_test.result import has_step + +def test_data_attachment_from_step(behave_runner: AllureBehaveRunner): + behave_runner.run_rst_example( + "data-attachment-feature", + steps=["data-attachment-steps"] + ) + assert_that( + behave_runner.allure_results, + has_test_case( + "Data attachment from step definitions", + has_step( + "Given a step that adds a named attachment", + has_attachment_with_content( + behave_runner.allure_results.attachments, + equal_to("This is the attachment with the name 'step.txt'"), + name="step.txt" + ) + ), + has_step( + "And a step that adds a typed named attachment", + has_attachment_with_content( + behave_runner.allure_results.attachments, + equal_to( + "[DEBUG] This attachment is named 'trace.log' and has " + "TEXT document appearance" + ), + attach_type="text/plain", + name="trace.log" + ) + ) + ) + ) + + +def test_file_attachment_from_step(behave_runner: AllureBehaveRunner): + behave_runner.run_rst_example( + "file-attachment-feature", + steps=["file-attachment-steps"] + ) + assert_that( + behave_runner.allure_results, + has_test_case( + "File attachment from a step definition", + has_step( + "Given a step that attaches a file", + has_attachment("text/plain", "web.log") + ) + ) + ) + + +def test_data_attachment_from_hook(behave_runner: AllureBehaveRunner): + feature = """ + Feature: Allure attachments in behave tests + Scenario: Attachment from after_scenario hook + Given noop + """ + behave_runner.run_rst_example( + feature_literals=[feature], + step_literals=["given('noop')(lambda _:0)"], + environment="attach-hook" + ) + assert_that( + behave_runner.allure_results, + has_test_case( + "Attachment from after_scenario hook", + has_attachment_with_content( + behave_runner.allure_results.attachments, + equal_to("This attachment will appear on a scenario level"), + attach_type="text/plain", + name="attachment.txt" + ) + ) + ) \ No newline at end of file diff --git a/tests/allure_behave/acceptance/description/description_test.py b/tests/allure_behave/acceptance/allure_api/description/description_test.py similarity index 100% rename from tests/allure_behave/acceptance/description/description_test.py rename to tests/allure_behave/acceptance/allure_api/description/description_test.py diff --git a/tests/allure_behave/acceptance/labels/label_test.py b/tests/allure_behave/acceptance/allure_api/labels/label_test.py similarity index 94% rename from tests/allure_behave/acceptance/labels/label_test.py rename to tests/allure_behave/acceptance/allure_api/labels/label_test.py index 66c8c4dd..4e5a9d3c 100644 --- a/tests/allure_behave/acceptance/labels/label_test.py +++ b/tests/allure_behave/acceptance/allure_api/labels/label_test.py @@ -1,7 +1,7 @@ """ ./allure-behave/examples/label.rst """ from tests.allure_behave.conftest import AllureBehaveRunner -from hamcrest import assert_that, all_of +from hamcrest import assert_that from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.label import has_label diff --git a/tests/allure_behave/acceptance/allure_api/links/link_test.py b/tests/allure_behave/acceptance/allure_api/links/link_test.py new file mode 100644 index 00000000..ddb3d12e --- /dev/null +++ b/tests/allure_behave/acceptance/allure_api/links/link_test.py @@ -0,0 +1,97 @@ +""" ./allure-behave/examples/link.rst """ + +from tests.allure_behave.conftest import AllureBehaveRunner +from hamcrest import assert_that, all_of +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status +from allure_commons_test.result import has_link +from allure_commons_test.result import has_issue_link +from allure_commons_test.result import has_test_case_link + +def test_link_on_scenario_level(behave_runner: AllureBehaveRunner): + behave_runner.run_rst_example( + "link-scenario-feature", + step_literals=["given('noop')(lambda c:None)"] + ) + assert_that( + behave_runner.allure_results, + has_test_case( + "Scenario with the link to the allure report website", + with_status("passed"), + has_link("https://qameta.io/allure-report/", "link", "report") + ) + ) + + +def test_link_on_feature_level(behave_runner: AllureBehaveRunner): + behave_runner.run_rst_example( + "link-feature-feature", + step_literals=["given('noop')(lambda c:None)"] + ) + assert_that( + behave_runner.allure_results, + all_of( + has_test_case( + "Scenario with the link to the homepage", + with_status("passed"), + has_link("https://qameta.io", "link", "homepage") + ), + has_test_case( + "Another scenario with the link to the homepage", + with_status("passed"), + has_link("https://qameta.io", "link", "homepage") + ) + ) + ) + +def test_specialized_links(behave_runner: AllureBehaveRunner): + behave_runner.run_rst_example( + "specialized-links-feature", + step_literals=["given('noop')(lambda c:None)"] + ) + assert_that( + behave_runner.allure_results, + all_of( + has_test_case( + "Scenario associated with the issue", + with_status("passed"), + has_issue_link( + "https://github.com/allure-framework/allure-python/issues/1", + "https://github.com/allure-framework/allure-python/issues/1" + ) + ), + has_test_case( + "Scenario associated with the TMS test case", + with_status("passed"), + has_test_case_link( + "https://qameta.io/#features", + "https://qameta.io/#features" + ) + ) + ) + ) + + +def test_dynamic_links(behave_runner: AllureBehaveRunner): + behave_runner.run_rst_example( + "dynamic-links-feature", + steps=["dynamic-links-steps"], + environment="dynamic-links-hooks" + ) + assert_that( + behave_runner.allure_results, + has_test_case( + "Issue from the step definition and link from the hook", + with_status("passed"), + has_issue_link( + "https://github.com/allure-framework/allure-python/issues/1", + "Skip None and empty values in json" + ) + , + has_link( + "https://qameta.io/allure-report/", + "link", + "Allure Report" + ) + ) + ) \ No newline at end of file diff --git a/tests/allure_behave/acceptance/allure_api/severities/severity_test.py b/tests/allure_behave/acceptance/allure_api/severities/severity_test.py new file mode 100644 index 00000000..de9aec5f --- /dev/null +++ b/tests/allure_behave/acceptance/allure_api/severities/severity_test.py @@ -0,0 +1,74 @@ +""" ./allure-behave/examples/severity.rst """ + +import pytest +from tests.allure_behave.conftest import AllureBehaveRunner +from hamcrest import assert_that, all_of +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status +from allure_commons_test.label import has_severity + + +@pytest.mark.parametrize(["name", "sev"], [ + pytest.param("Blocking scenario", "blocker", id="blocker"), + pytest.param("Critical scenario", "critical", id="critical"), + pytest.param("Normal scenario", "normal", id="normal"), + pytest.param("Minor scenario", "minor", id="minor"), + pytest.param("Trivial scenario", "trivial", id="trivial") +]) +def test_severity_on_scenario(name, sev, behave_runner: AllureBehaveRunner): + behave_runner.run_rst_example( + "severity-on-scenario", + step_literals=["given('noop')(lambda c:None)"] + ) + assert_that( + behave_runner.allure_results, + has_test_case( + name, + with_status("passed"), + has_severity(sev) + ) + ) + + +def test_severity_on_feature(behave_runner: AllureBehaveRunner): + behave_runner.run_rst_example( + "severity-on-feature", + step_literals=["given('noop')(lambda c:None)"] + ) + assert_that( + behave_runner.allure_results, + all_of( + has_test_case( + "This scenario inherits the @cricial tag", + with_status("passed"), + has_severity("critical") + ), + has_test_case( + "This scenario also inherits the @cricial tag", + with_status("passed"), + has_severity("critical") + ) + ) + ) + + +def test_multiple_severity_tags(behave_runner: AllureBehaveRunner): + behave_runner.run_rst_example( + "multiple-severities", + step_literals=["given('noop')(lambda c:None)"] + ) + assert_that( + behave_runner.allure_results, + all_of( + has_test_case( + "This is a trivial scenario", + with_status("passed"), + has_severity("trivial") + ), + has_test_case( + "While this one is a blocker", + with_status("passed"), + has_severity("blocker") + ) + ) + ) diff --git a/tests/allure_behave/acceptance/allure_api/tags/tag_test.py b/tests/allure_behave/acceptance/allure_api/tags/tag_test.py new file mode 100644 index 00000000..d077aa95 --- /dev/null +++ b/tests/allure_behave/acceptance/allure_api/tags/tag_test.py @@ -0,0 +1,46 @@ +""" ./allure-behave/examples/tag.rst """ + +import pytest +from tests.allure_behave.conftest import AllureBehaveRunner +from hamcrest import assert_that +from allure_commons_test.report import has_test_case +from allure_commons_test.label import has_tag + + +@pytest.mark.parametrize(["feature_id", "scenario", "tags"], [ + pytest.param( + "tag-scenario-feature", + "Applying a tag directly to a scenario", + ["distributed"], + id="scenario-tag" + ), + pytest.param( + "tag-feature-feature", + "Applying a tag to a feature", + ["isolated"], + id="feature-tag" + ), + pytest.param( + "tag-multiple-feature", + "Applying multiple tags", + ["node-1", "node-2", "node-3", "node-4"], + id="multiple-tags" + ) +]) +def test_behave_tags_as_allure_tags( + feature_id, + scenario, + tags, + behave_runner: AllureBehaveRunner +): + behave_runner.run_rst_example( + feature_id, + step_literals=["given('noop')(lambda c:None)"] + ) + assert_that( + behave_runner.allure_results, + has_test_case( + scenario, + *(has_tag(tag) for tag in tags) + ) + ) \ No newline at end of file diff --git a/tests/allure_behave/acceptance/allure_api/testplan/testplan_test.py b/tests/allure_behave/acceptance/allure_api/testplan/testplan_test.py new file mode 100644 index 00000000..825c2174 --- /dev/null +++ b/tests/allure_behave/acceptance/allure_api/testplan/testplan_test.py @@ -0,0 +1,138 @@ +""" ./allure-behave/examples/testplan.rst """ + +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status +from hamcrest import assert_that, all_of, not_ +from pytest import MonkeyPatch +from tests.allure_behave.conftest import AllureBehaveRunner +from tests.conftest import RstExampleTable + +def test_testplan_fullname_selection( + monkeypatch: MonkeyPatch, + rst_examples: RstExampleTable, + behave_runner: AllureBehaveRunner +): + monkeypatch.setenv( + "ALLURE_TESTPLAN_PATH", + str( + behave_runner.pytester.makefile( + ".json", + rst_examples["fullname-testplan"] + ) + ) + ) + + behave_runner.run_rst_example( + "fullname-feature-1", + "fullname-feature-2", + steps=["steps"] + ) + + assert_that( + behave_runner.allure_results, + all_of( + has_test_case( + "Scenario selection", + with_status("passed") + ), + has_test_case( + "Scenario deselection", + with_status("skipped") + ), + has_test_case( + "Scenario selection 2", + with_status("passed") + ), + has_test_case( + "Scenario deselection 2", + with_status("skipped") + ) + ) + ) + + +def test_testplan_id_selection( + monkeypatch: MonkeyPatch, + rst_examples: RstExampleTable, + behave_runner: AllureBehaveRunner +): + monkeypatch.setenv( + "ALLURE_TESTPLAN_PATH", + str( + behave_runner.pytester.makefile( + ".json", + rst_examples["id-testplan"] + ) + ) + ) + + behave_runner.run_rst_example( + "id-feature-1", + "id-feature-2", + steps=["steps"] + ) + + assert_that( + behave_runner.allure_results, + all_of( + has_test_case( + "Scenario selection", + with_status("passed") + ), + has_test_case( + "Scenario deselection", + with_status("skipped") + ), + has_test_case( + "Scenario selection 2", + with_status("passed") + ), + has_test_case( + "Scenario deselection 2", + with_status("skipped") + ) + ) + ) + + +def test_skipping_of_tests_missing_in_testplan( + monkeypatch: MonkeyPatch, + rst_examples: RstExampleTable, + behave_runner: AllureBehaveRunner +): + monkeypatch.setenv( + "ALLURE_TESTPLAN_PATH", + str( + behave_runner.pytester.makefile( + ".json", + rst_examples["fullname-testplan"] + ) + ) + ) + + behave_runner.run_rst_example( + "fullname-feature-1", + "fullname-feature-2", + steps=["steps"], + cli_args=["-D", "AllureFormatter.hide_excluded=True"] + ) + + assert_that( + behave_runner.allure_results, + all_of( + has_test_case( + "Scenario selection", + with_status("passed") + ), + not_( + has_test_case("Scenario deselection") + ), + has_test_case( + "Scenario selection 2", + with_status("passed") + ), + not_( + has_test_case("Scenario deselection 2") + ) + ) + ) diff --git a/tests/allure_behave/acceptance/background/background_steps.py b/tests/allure_behave/acceptance/behave_support/background/background_steps.py similarity index 100% rename from tests/allure_behave/acceptance/background/background_steps.py rename to tests/allure_behave/acceptance/behave_support/background/background_steps.py diff --git a/tests/allure_behave/acceptance/background/background_test.py b/tests/allure_behave/acceptance/behave_support/background/background_test.py similarity index 50% rename from tests/allure_behave/acceptance/background/background_test.py rename to tests/allure_behave/acceptance/behave_support/background/background_test.py index 4aa3e540..d1357468 100644 --- a/tests/allure_behave/acceptance/background/background_test.py +++ b/tests/allure_behave/acceptance/behave_support/background/background_test.py @@ -1,5 +1,5 @@ import pytest -from hamcrest import assert_that +from hamcrest import assert_that, all_of from tests.allure_behave.conftest import AllureBehaveRunner from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status @@ -40,40 +40,42 @@ def test_background( ) assert_that( behave_runner.allure_results, - has_test_case( - f"Scenario with background containing {step_outcome} step", - with_status(status), - has_step( - f"Given the first background step that is {step_outcome}", - with_status(status) + all_of( + has_test_case( + f"Scenario with background containing {step_outcome} step", + with_status(status), + has_step( + f"Given the first background step that is {step_outcome}", + with_status(status) + ), + has_step( + "And the second background step with no failures", + with_status(remained_steps_status) + ), + has_step( + "Given the first step with no failures", + with_status(remained_steps_status) + ), + has_step( + "And the second step with no failures", + with_status(remained_steps_status) + ) ), - has_step( - "And the second background step with no failures", - with_status(remained_steps_status) - ), - has_step( - "Given the first step with no failures", - with_status(remained_steps_status) - ), - has_step( - "And the second step with no failures", - with_status(remained_steps_status) - ) - ), - has_test_case( - f"Another scenario with background containing {step_outcome} step", - with_status(status), - has_step( - f"Given the first background step that is {step_outcome}", - with_status(status) - ), - has_step( - "And the second background step with no failures", - with_status(remained_steps_status) - ), - has_step( - "Given the step with no failures", - with_status(remained_steps_status) + has_test_case( + f"Another scenario with background containing {step_outcome} step", + with_status(status), + has_step( + f"Given the first background step that is {step_outcome}", + with_status(status) + ), + has_step( + "And the second background step with no failures", + with_status(remained_steps_status) + ), + has_step( + "Given the step with no failures", + with_status(remained_steps_status) + ) ) ) ) \ No newline at end of file diff --git a/tests/allure_behave/acceptance/behave_support/behave_cmd/behave_cmd_test.py b/tests/allure_behave/acceptance/behave_support/behave_cmd/behave_cmd_test.py new file mode 100644 index 00000000..5a157481 --- /dev/null +++ b/tests/allure_behave/acceptance/behave_support/behave_cmd/behave_cmd_test.py @@ -0,0 +1,63 @@ +from hamcrest import assert_that, all_of, not_ +from tests.allure_behave.conftest import AllureBehaveRunner +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status + +def test_behave_tags_filter(docstring: str, behave_runner: AllureBehaveRunner): + """Feature: Behave --tags CLI argument support + + @tag + Scenario: Scenario with tag + Given noop + + Scenario: Scenario without tag + Given noop + """ + + behave_runner.run_behave( + features=[docstring], + steps=["given('noop')(lambda c:None)"], + cli_args=["--tags=tag"] + ) + assert_that( + behave_runner.allure_results, + all_of( + has_test_case( + "Scenario with tag", + with_status("passed") + ), + has_test_case( + "Scenario without tag", + with_status("skipped") + ) + ) + ) + + +def test_behave_no_skipped_support(docstring: str, behave_runner: AllureBehaveRunner): + """Feature: Behave --tags CLI argument support + + @tag + Scenario: Scenario with tag + Given noop + + Scenario: Scenario without tag + Given noop + """ + behave_runner.run_behave( + features=[docstring], + steps=["given('noop')(lambda c:None)"], + cli_args=["--tags=tag", "--no-skipped"] + ) + assert_that( + behave_runner.allure_results, + all_of( + has_test_case( + "Scenario with tag", + with_status("passed") + ), + not_( + has_test_case("Scenario without tag") + ) + ) + ) diff --git a/tests/allure_behave/acceptance/hooks/hook_test.py b/tests/allure_behave/acceptance/behave_support/hooks/hook_test.py similarity index 79% rename from tests/allure_behave/acceptance/hooks/hook_test.py rename to tests/allure_behave/acceptance/behave_support/hooks/hook_test.py index 537ccb39..7054778e 100644 --- a/tests/allure_behave/acceptance/hooks/hook_test.py +++ b/tests/allure_behave/acceptance/behave_support/hooks/hook_test.py @@ -164,30 +164,28 @@ def test_context_step_in_scenario_hooks(behave_runner: Runner): ) assert_that( behave_runner.allure_results, - all_of( - has_test_case( - "Hook with steps", - with_status("passed"), - has_container( - behave_runner.allure_results, - has_before( - "before scenario", - with_status("passed"), - has_step( - "Step in before_scenario", - with_status("passed") - ) - ), + has_test_case( + "Hook with steps", + with_status("passed"), + has_container( + behave_runner.allure_results, + has_before( + "before scenario", + with_status("passed"), + has_step( + "Step in before_scenario", + with_status("passed") + ) ), - has_container( - behave_runner.allure_results, - has_after( - "after scenario", - with_status("passed"), - has_step( - "Step in after_scenario", - with_status("passed") - ) + ), + has_container( + behave_runner.allure_results, + has_after( + "after scenario", + with_status("passed"), + has_step( + "Step in after_scenario", + with_status("passed") ) ) ) @@ -203,30 +201,28 @@ def test_func_step_in_scenario_hooks(behave_runner: Runner): ) assert_that( behave_runner.allure_results, - all_of( - has_test_case( - "Hook with steps", - with_status("passed"), - has_container( - behave_runner.allure_results, - has_before( - "before all", - with_status("passed"), - has_step( - "Step in 'before_all'", - with_status("passed") - ) - ), + has_test_case( + "Hook with steps", + with_status("passed"), + has_container( + behave_runner.allure_results, + has_before( + "before all", + with_status("passed"), + has_step( + "Step in 'before_all'", + with_status("passed") + ) ), - has_container( - behave_runner.allure_results, - has_after( - "after all", - with_status("passed"), - has_step( - "Step in 'after_all'", - with_status("passed") - ) + ), + has_container( + behave_runner.allure_results, + has_after( + "after all", + with_status("passed"), + has_step( + "Step in 'after_all'", + with_status("passed") ) ) ) diff --git a/tests/allure_behave/acceptance/hooks/test-data/attachment-hook.feature b/tests/allure_behave/acceptance/behave_support/hooks/test-data/attachment-hook.feature similarity index 100% rename from tests/allure_behave/acceptance/hooks/test-data/attachment-hook.feature rename to tests/allure_behave/acceptance/behave_support/hooks/test-data/attachment-hook.feature diff --git a/tests/allure_behave/acceptance/hooks/test-data/attachment-hooks.py b/tests/allure_behave/acceptance/behave_support/hooks/test-data/attachment-hooks.py similarity index 100% rename from tests/allure_behave/acceptance/hooks/test-data/attachment-hooks.py rename to tests/allure_behave/acceptance/behave_support/hooks/test-data/attachment-hooks.py diff --git a/tests/allure_behave/acceptance/hooks/test-data/context-step-hooks.py b/tests/allure_behave/acceptance/behave_support/hooks/test-data/context-step-hooks.py similarity index 100% rename from tests/allure_behave/acceptance/hooks/test-data/context-step-hooks.py rename to tests/allure_behave/acceptance/behave_support/hooks/test-data/context-step-hooks.py diff --git a/tests/allure_behave/acceptance/hooks/test-data/feature-tag-hook.feature b/tests/allure_behave/acceptance/behave_support/hooks/test-data/feature-tag-hook.feature similarity index 100% rename from tests/allure_behave/acceptance/hooks/test-data/feature-tag-hook.feature rename to tests/allure_behave/acceptance/behave_support/hooks/test-data/feature-tag-hook.feature diff --git a/tests/allure_behave/acceptance/hooks/test-data/func-step-hooks.py b/tests/allure_behave/acceptance/behave_support/hooks/test-data/func-step-hooks.py similarity index 100% rename from tests/allure_behave/acceptance/hooks/test-data/func-step-hooks.py rename to tests/allure_behave/acceptance/behave_support/hooks/test-data/func-step-hooks.py diff --git a/tests/allure_behave/acceptance/hooks/test-data/global-hooks.feature b/tests/allure_behave/acceptance/behave_support/hooks/test-data/global-hooks.feature similarity index 100% rename from tests/allure_behave/acceptance/hooks/test-data/global-hooks.feature rename to tests/allure_behave/acceptance/behave_support/hooks/test-data/global-hooks.feature diff --git a/tests/allure_behave/acceptance/hooks/test-data/global-hooks.py b/tests/allure_behave/acceptance/behave_support/hooks/test-data/global-hooks.py similarity index 100% rename from tests/allure_behave/acceptance/hooks/test-data/global-hooks.py rename to tests/allure_behave/acceptance/behave_support/hooks/test-data/global-hooks.py diff --git a/tests/allure_behave/acceptance/hooks/test-data/step-hook.feature b/tests/allure_behave/acceptance/behave_support/hooks/test-data/step-hook.feature similarity index 100% rename from tests/allure_behave/acceptance/hooks/test-data/step-hook.feature rename to tests/allure_behave/acceptance/behave_support/hooks/test-data/step-hook.feature diff --git a/tests/allure_behave/acceptance/hooks/test-data/steps.py b/tests/allure_behave/acceptance/behave_support/hooks/test-data/steps.py similarity index 100% rename from tests/allure_behave/acceptance/hooks/test-data/steps.py rename to tests/allure_behave/acceptance/behave_support/hooks/test-data/steps.py diff --git a/tests/allure_behave/acceptance/hooks/test-data/tag-hook.feature b/tests/allure_behave/acceptance/behave_support/hooks/test-data/tag-hook.feature similarity index 100% rename from tests/allure_behave/acceptance/hooks/test-data/tag-hook.feature rename to tests/allure_behave/acceptance/behave_support/hooks/test-data/tag-hook.feature diff --git a/tests/allure_behave/acceptance/hooks/test-data/tag-hooks.py b/tests/allure_behave/acceptance/behave_support/hooks/test-data/tag-hooks.py similarity index 100% rename from tests/allure_behave/acceptance/hooks/test-data/tag-hooks.py rename to tests/allure_behave/acceptance/behave_support/hooks/test-data/tag-hooks.py diff --git a/tests/allure_behave/acceptance/behave_support/scenario_outlines/scenario_outline_test.py b/tests/allure_behave/acceptance/behave_support/scenario_outlines/scenario_outline_test.py new file mode 100644 index 00000000..1bc8a49f --- /dev/null +++ b/tests/allure_behave/acceptance/behave_support/scenario_outlines/scenario_outline_test.py @@ -0,0 +1,136 @@ +from hamcrest import assert_that, all_of, has_entry +from tests.allure_behave.conftest import AllureBehaveRunner +from allure_commons_test.report import has_only_testcases +from allure_commons_test.result import with_status +from allure_commons_test.result import has_parameter + +def test_outline_with_single_table(behave_runner: AllureBehaveRunner): + behave_runner.run_behave( + feature_paths=["./test-data/outline.feature"], + step_paths=["./test-data/steps.py"], + cli_args=["--tags=1", "--no-skipped"] + ) + + assert_that( + behave_runner.allure_results, + has_only_testcases( + all_of( + has_entry( + "name", + "Scenario outline with one table -- @1.1 Customers" + ), + with_status("passed"), + has_parameter("name", "Alice"), + has_parameter("surname", "Johnson") + ), + all_of( + has_entry( + "name", + "Scenario outline with one table -- @1.2 Customers" + ), + with_status("passed"), + has_parameter("name", "Bob"), + has_parameter("surname", "Smith") + ) + ) + ) + + +def test_outline_with_multiple_tables(behave_runner: AllureBehaveRunner): + behave_runner.run_behave( + feature_paths=["./test-data/outline.feature"], + step_paths=["./test-data/steps.py"], + cli_args=["--tags=multiple-tables", "--no-skipped"] + ) + + assert_that( + behave_runner.allure_results, + has_only_testcases( + all_of( + has_entry( + "name", + "Scenario outline with multiple tables -- @1.1 Customers" + ), + with_status("passed"), + has_parameter("name", "Alice"), + has_parameter("surname", "Johnson") + ), + all_of( + has_entry( + "name", + "Scenario outline with multiple tables -- @1.2 Customers" + ), + with_status("passed"), + has_parameter("name", "Bob"), + has_parameter("surname", "Smith") + ), + all_of( + has_entry( + "name", + "Scenario outline with multiple tables -- @2.1 Employees" + ), + with_status("passed"), + has_parameter("name", "Jane"), + has_parameter("surname", "Watson") + ), + all_of( + has_entry( + "name", + "Scenario outline with multiple tables -- @2.2 Employees" + ), + with_status("passed"), + has_parameter("name", "Mark"), + has_parameter("surname", "Nickson") + ) + ) + ) + + +def test_multiple_outlines_each_with_one_table(behave_runner: AllureBehaveRunner): + behave_runner.run_behave( + feature_paths=["./test-data/outline.feature"], + step_paths=["./test-data/steps.py"], + cli_args=["--tags=single-table", "--no-skipped"] + ) + + assert_that( + behave_runner.allure_results, + has_only_testcases( + all_of( + has_entry( + "name", + "Scenario outline with one table -- @1.1 Customers" + ), + with_status("passed"), + has_parameter("name", "Alice"), + has_parameter("surname", "Johnson") + ), + all_of( + has_entry( + "name", + "Scenario outline with one table -- @1.2 Customers" + ), + with_status("passed"), + has_parameter("name", "Bob"), + has_parameter("surname", "Smith") + ), + all_of( + has_entry( + "name", + "Another scenario outline with one table -- @1.1 Employees" + ), + with_status("passed"), + has_parameter("name", "Jane"), + has_parameter("surname", "Watson") + ), + all_of( + has_entry( + "name", + "Another scenario outline with one table -- @1.2 Employees" + ), + with_status("passed"), + has_parameter("name", "Mark"), + has_parameter("surname", "Nickson") + ) + ) + ) diff --git a/tests/allure_behave/acceptance/behave_support/scenario_outlines/test-data/outline.feature b/tests/allure_behave/acceptance/behave_support/scenario_outlines/test-data/outline.feature new file mode 100644 index 00000000..99518f81 --- /dev/null +++ b/tests/allure_behave/acceptance/behave_support/scenario_outlines/test-data/outline.feature @@ -0,0 +1,34 @@ +Feature: Scenario Outline + @single-table + @1 + Scenario Outline: Scenario outline with one table + Given a user + + Examples: Customers + | name | surname | + | Alice | Johnson | + | Bob | Smith | + + @single-table + @2 + Scenario Outline: Another scenario outline with one table + Given a user + + Examples: Employees + | name | surname | + | Jane | Watson | + | Mark | Nickson | + + @multiple-tables + Scenario Outline: Scenario outline with multiple tables + Given a user + + Examples: Customers + | name | surname | + | Alice | Johnson | + | Bob | Smith | + + Examples: Employees + | name | surname | + | Jane | Watson | + | Mark | Nickson | diff --git a/tests/allure_behave/acceptance/behave_support/scenario_outlines/test-data/steps.py b/tests/allure_behave/acceptance/behave_support/scenario_outlines/test-data/steps.py new file mode 100644 index 00000000..d80a758b --- /dev/null +++ b/tests/allure_behave/acceptance/behave_support/scenario_outlines/test-data/steps.py @@ -0,0 +1,5 @@ +from behave import given + +@given("a user {name} {surname}") +def step_impl(context, name, surname): + pass diff --git a/tests/allure_behave/acceptance/behave_support/scenarios/scenario_test.py b/tests/allure_behave/acceptance/behave_support/scenarios/scenario_test.py new file mode 100644 index 00000000..28ad6ff9 --- /dev/null +++ b/tests/allure_behave/acceptance/behave_support/scenarios/scenario_test.py @@ -0,0 +1,143 @@ +import pytest +from textwrap import dedent +from hamcrest import assert_that +from tests.allure_behave.conftest import AllureBehaveRunner +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status +from allure_commons_test.result import has_step + + +@pytest.mark.parametrize(["step", "status"], [ + pytest.param( + "given('a step')(lambda c:None)", + "passed", + id="passed" + ), + pytest.param( + "@given('a step')\ndef step_impl(_):assert False", + "failed", + id="failed" + ), + pytest.param( + "@given('a step')\ndef step_impl(_):raise ValueError", + "broken", + id="broken" + ), + pytest.param( + "", + "broken", + id="undefined" + ) +]) +def test_scenario_with_one_step( + docstring, + behave_runner: AllureBehaveRunner, + step, + status +): + """ + Feature: Behave scenario support + Scenario: Scenario with single step + Given a step + """ + + behave_runner.run_behave( + features=[docstring], + steps=[step] + ) + assert_that( + behave_runner.allure_results, + has_test_case( + "Scenario with single step", + with_status(status), + has_step( + "Given a step", + with_status(status) + ) + ) + ) + + +@pytest.mark.parametrize(["trigger_step", "status"], [ + pytest.param( + "@given('trigger')\ndef _(_):assert False", + "failed", + id="failed" + ), + pytest.param( + "@given('trigger')\ndef _(_):raise ValueError", + "broken", + id="broken" + ), + pytest.param( + "", + "broken", + id="undefined" + ) +]) +def test_when_not_passed_remaining_steps_are_skipped( + docstring, + behave_runner: AllureBehaveRunner, + trigger_step, + status +): + """ + Feature: Behave scenario support + Scenario: Scenario with four steps + Given step 1 + And trigger + And step 3 + And step 4 + """ + + behave_runner.run_behave( + features=[docstring], + steps=[ + "given('step {n}')(lambda c,**_:None)", + trigger_step + ] + ) + assert_that( + behave_runner.allure_results, + has_test_case( + "Scenario with four steps", + with_status(status), + has_step( + "Given step 1", + with_status("passed") + ), + has_step( + "And trigger", + with_status(status) + ), + has_step( + "And step 3", + with_status("skipped") + ), + has_step( + "And step 4", + with_status("skipped") + ) + ) + ) + + +def test_nameless_scenario(docstring, behave_runner: AllureBehaveRunner): + """ + Feature: Behave scenario support + Scenario: + Given noop + """ + + behave_runner.run_behave( + features=[docstring], + steps=["given('noop')(lambda c:None)"] + ) + + assert_that( + behave_runner.allure_results, + has_test_case( + "Scenario", + with_status("passed") + ) + ) \ No newline at end of file diff --git a/tests/allure_behave/acceptance/behave_support/steps/behave_step_test.py b/tests/allure_behave/acceptance/behave_support/steps/behave_step_test.py new file mode 100644 index 00000000..1185612b --- /dev/null +++ b/tests/allure_behave/acceptance/behave_support/steps/behave_step_test.py @@ -0,0 +1,172 @@ +import allure +from tests.allure_behave.conftest import AllureBehaveRunner +from hamcrest import assert_that, equal_to +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status +from allure_commons_test.result import has_step +from allure_commons_test.result import has_status_details +from allure_commons_test.result import with_message_contains +from allure_commons_test.result import has_attachment_with_content +from allure_commons_test.content import csv_equivalent + +def test_failed_behave_step(docstring:str, behave_runner: AllureBehaveRunner): + """ + Feature: Bheave step support + Scenario: Scenario with failed step + Given a step failed + """ + + behave_runner.run_behave( + features=[docstring], + steps=["@given('a step failed')\ndef _(_):assert False,'Fail message'"] + ) + + assert_that( + behave_runner.allure_results, + has_test_case( + "Scenario with failed step", + with_status("failed"), + has_step( + "Given a step failed", + with_status("failed"), + has_status_details( + with_message_contains("AssertionError: Fail message") + ) + ) + ) + ) + +def test_broken_behave_step(docstring:str, behave_runner: AllureBehaveRunner): + """ + Feature: Bheave step support + Scenario: Scenario with broken step + Given a broken step + """ + + behave_runner.run_behave( + features=[docstring], + steps=["@given('a broken step')\ndef _(_):raise ValueError('Reason')"] + ) + + assert_that( + behave_runner.allure_results, + has_test_case( + "Scenario with broken step", + with_status("broken"), + has_step( + "Given a broken step", + with_status("broken"), + has_status_details( + with_message_contains("ValueError: Reason") + ) + ) + ) + ) + + +def test_step_text_data(docstring:str, behave_runner: AllureBehaveRunner): + """ + Feature: Bheave step support + Scenario: Scenario with step which contains text data + Given a step with text data + ''' + Textual information attached to the step. + ''' + """ + + behave_runner.run_behave( + features=[docstring], + steps=["given('a step with text data')(lambda _:0)"] + ) + + assert_that( + behave_runner.allure_results, + has_test_case( + "Scenario with step which contains text data", + with_status("passed"), + has_step( + "Given a step with text data", + with_status("passed"), + has_attachment_with_content( + behave_runner.allure_results.attachments, + equal_to("Textual information attached to the step."), + "text/plain" + ) + ) + ) + ) + + +def test_step_table_data(docstring:str, behave_runner: AllureBehaveRunner): + """ + Feature: Bheave step support + Scenario: Scenario with step which contains a table + Given a step with table data + | id | name | + | 1 | John | + | 2 | Jane | + """ + + behave_runner.run_behave( + features=[docstring], + steps=["given('a step with table data')(lambda _:0)"] + ) + + assert_that( + behave_runner.allure_results, + has_test_case( + "Scenario with step which contains a table", + with_status("passed"), + has_step( + "Given a step with table data", + with_status("passed"), + has_attachment_with_content( + behave_runner.allure_results.attachments, + csv_equivalent([ + ["id", "name"], + ["1", "John"], + ["2", "Jane"] + ]), + "text/csv" + ) + ) + ) + ) + + +@allure.issue("https://github.com/allure-framework/allure-python/issues/717") +def test_step_table_data_escaping( + docstring:str, + behave_runner: AllureBehaveRunner +): + """ + Feature: Bheave step support + Scenario: Csv special symbols in step's table data + Given a step table data with special symbols + | Item A | Item B | + | Item "1", Item 2 | Item 3," Item 4 | + """ + + behave_runner.run_behave( + features=[docstring], + steps=["given('a step table data with special symbols')(lambda _:0)"] + ) + + assert_that( + behave_runner.allure_results, + has_test_case( + "Csv special symbols in step's table data", + with_status("passed"), + has_step( + "Given a step table data with special symbols", + has_attachment_with_content( + behave_runner.allure_results.attachments, + csv_equivalent([ + ["Item A", "Item B"], + ["Item \"1\", Item 2", "Item 3,\" Item 4"] + ]), + "text/csv" + ) + ) + ) + ) diff --git a/tests/allure_behave/conftest.py b/tests/allure_behave/conftest.py index 3fd79038..2a95b6e0 100644 --- a/tests/allure_behave/conftest.py +++ b/tests/allure_behave/conftest.py @@ -1,11 +1,8 @@ -import shutil import sys import behave.step_registry from itertools import chain from pathlib import Path from pytest import FixtureRequest, fixture, Pytester -from tests.conftest import AllureIntegrationRunner -from tests.conftest import get_path_from_docstring from tests.conftest import fake_logger from tests.conftest import RstExampleTable from behave.runner import Runner @@ -52,9 +49,14 @@ def __fixed_add_step_definition(self, *args, **kwargs): ) StepRegistry.add_step_definition = __fixed_add_step_definition -class InMemoryBehaveRunner(Runner): - def __init__(self, features, steps, environment): - config = Configuration(["--no-snippets"], load_config=False) +class _InMemoryBehaveRunner(Runner): + def __init__(self, features, steps, environment, args=None): + if args is None: + args = [] + config = Configuration( + ["--no-snippets"] + list(args), + load_config=False + ) super().__init__(config) self.__features = features self.__steps = steps @@ -107,7 +109,6 @@ class AllureBehaveRunner: def __init__(self, pytester: Pytester, request: FixtureRequest): self.pytester = pytester self.request = request - self.runner = AllureIntegrationRunner("behave") self.exit_code = None self.allure_results = None @@ -117,9 +118,10 @@ def run_behave( features: Sequence[str] = None, steps: Sequence[str] = None, environment: str = None, - feature_paths: Sequence[str] = None, - step_paths: Sequence[str] = None, - environment_path: str = None, + feature_paths: Sequence[str|Path] = None, + step_paths: Sequence[str|Path] = None, + environment_path: str|Path = None, + cli_args: Sequence[str] = None ) -> None: """Runs behave against specific set of features, steps and an environment each specified either as a string literal or as a path to a @@ -132,9 +134,11 @@ def run_behave( content of a step definition file. environment (str): a string representing content of the environment.py file. - feature_paths (Sequence[str]): a sequence of feature files. - step_paths (Sequence[str]): a sequence of step definition files. - environment_path (str): a path to the environment.py file. + feature_paths (Sequence[str|Path]): a sequence of feature files. + step_paths (Sequence[str|Path]): a sequence of step definition + files. + environment_path (str|Path): a path to the environment.py file. + cli_args (Sequence[str]): a CLI arguments passed to behave. The results of the run could be accessed through the :code:`allure_results` attribute. @@ -150,7 +154,7 @@ def run_behave( environment_path ) with fake_logger(path_to_fake) as allure_results: - InMemoryBehaveRunner(features, steps, environment).run() + _InMemoryBehaveRunner(features, steps, environment, cli_args).run() self.allure_results = allure_results def run_rst_example( @@ -160,7 +164,8 @@ def run_rst_example( feature_literals: Sequence[str] = None, step_literals: Sequence[str] = None, environment: str = None, - environment_literal: str = None + environment_literal: str = None, + cli_args: Sequence[str] = None ) -> None: """Loads code blocks from reStructuredText document and executes behave against them. @@ -203,7 +208,8 @@ def run_rst_example( step_literals ), environment=examples_table[environment] if environment else\ - environment_literal + environment_literal, + cli_args=cli_args ) @staticmethod @@ -249,9 +255,4 @@ def behave_runner(pytester: Pytester, request: FixtureRequest): return AllureBehaveRunner(pytester, request) -@fixture -def executed_docstring_path(allure_behave_runner): - allure_behave_runner.run_feature_by_docstring_path() - return allure_behave_runner - -__fix_behave_in_memory_run() \ No newline at end of file +__fix_behave_in_memory_run() diff --git a/tests/conftest.py b/tests/conftest.py index da833df0..4b058242 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -55,9 +55,31 @@ def get_docstring(node: pytest.Item) -> str: return None +def find_node_with_docstring( + request: FixtureRequest +) -> Tuple[pytest.Item, str]: + node = request.node + while node: + docstring = get_docstring(node) + if docstring: + break + node = node.parent + return node, docstring + + +def find_node_with_docstring_or_throw( + request: FixtureRequest +) -> Tuple[pytest.Item, str]: + node, docstring = find_node_with_docstring(request) + if node is None: + nodeid = request.node.nodeid + raise ValueError(f"Unable to get docstring for node {nodeid}") + return node, docstring + + def get_path_from_docstring(request: FixtureRequest) -> Path: return request.config.rootpath.joinpath( - get_docstring(request.node).strip() + find_node_with_docstring_or_throw(request)[1].strip() ) RstExampleTableT = TypeVar("RstExampleTableT", bound="RstExampleTable") @@ -103,7 +125,7 @@ def find_examples(request: FixtureRequest) -> RstExampleTableT: from the same document. """ - node, docstring = RstExampleTable.find_node_with_docstring_or_throw( + node, docstring = find_node_with_docstring_or_throw( request ) examples = RstExampleTable.__get_from_cache(node) @@ -111,28 +133,6 @@ def find_examples(request: FixtureRequest) -> RstExampleTableT: examples = RstExampleTable.__create_and_cache(node, docstring) return examples - @staticmethod - def find_node_with_docstring( - request: FixtureRequest - ) -> Tuple[pytest.Item, str]: - node = request.node - while node: - docstring = get_docstring(node) - if docstring: - break - node = node.parent - return node, docstring - - @staticmethod - def find_node_with_docstring_or_throw( - request: FixtureRequest - ) -> Tuple[pytest.Item, str]: - node, docstring = RstExampleTable.find_node_with_docstring(request) - if node is None: - nodeid = request.node.nodeid - raise ValueError(f"Unable to get docstring for node {nodeid}") - return node, docstring - @staticmethod def load_examples(filepath: str|Path) -> Mapping[str, str]: document = RstExampleTable.parse_rst(filepath) @@ -206,12 +206,12 @@ def get_plugin_args(self): yield f"no:{plugin_package}" def parse_docstring_source(self): - docstring = self.request.node.function.__doc__ or self.request.node.module.__doc__ + _, docstring = find_node_with_docstring_or_throw(self.request) source = script_from_examples(docstring).replace("#\n", "\n") return self.testdir.makepyfile(source) def parse_docstring_path(self): - example_file = self.__get_example_path() + example_file = get_path_from_docstring(self.request) with open(example_file, encoding="utf-8") as f: content = f.read() source = script_from_examples(content) @@ -243,29 +243,6 @@ def run_with_allure(self, *args: str, **kwargs: str): return self.allure_report - def __get_example_path(self): - return self.request.config.rootdir.join( - ( - self.request.node.function.__doc__ or - self.request.node.module.__doc__ - ).strip() - ) - - -class AllureIntegrationRunner: - def __init__(self, module: str, entry_point :str = "main") -> None: - self.__module_name = module - self.__entry_point = entry_point - - def run_allure_integration(self, *args, **kwargs): - with fake_logger() as allure_results: - module = runpy.run_module(self.__module_name) - return_value = module[self.__entry_point](*args, **kwargs) - return { - "allure-results": allure_results, - "return-value": return_value - } - @pytest.fixture def allured_testdir( @@ -305,4 +282,9 @@ def executed_docstring_directory( @pytest.fixture def rst_examples(request: FixtureRequest) -> RstExampleTable: - return RstExampleTable.find_examples(request) \ No newline at end of file + return RstExampleTable.find_examples(request) + + +@pytest.fixture +def docstring(request: FixtureRequest) -> str: + return find_node_with_docstring_or_throw(request)[1] From d8b919f2e08cd5a121687536d59a74ead8cbd701 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Tue, 14 Feb 2023 01:40:45 +0700 Subject: [PATCH 04/18] Complete behave. Start robot: runner, attachments --- allure-behave/pyproject.toml | 3 +- allure-pytest-bdd/pyproject.toml | 4 +- allure-pytest/pyproject.toml | 4 +- allure-pytest/src/listener.py | 13 +- allure-python-commons/src/logger.py | 2 +- allure-robotframework/README.rst | 2 +- .../examples/attach/data_attach.rst | 21 -- .../examples/attach/file_attach.rst | 8 - .../examples/attach/foreign_library.py | 15 -- .../attach/foreign_library_attach.rst | 22 -- .../examples/attach/foreign_library_helper.py | 22 -- allure-robotframework/examples/attachment.rst | 138 ++++++++++ allure-robotframework/pyproject.toml | 5 +- tests/allure_behave/__init__.py | 0 tests/allure_behave/acceptance/__init__.py | 0 .../acceptance/allure_api/__init__.py | 0 .../allure_api/attachment/__init__.py | 0 .../allure_api/description/__init__.py | 0 .../acceptance/allure_api/labels/__init__.py | 0 .../acceptance/allure_api/links/__init__.py | 0 .../allure_api/severities/__init__.py | 0 .../acceptance/allure_api/tags/__init__.py | 0 .../allure_api/testplan/__init__.py | 0 .../acceptance/behave_support/__init__.py | 0 .../behave_support/background/__init__.py | 0 .../behave_support/behave_cmd/__init__.py | 0 .../behave_support/hooks/__init__.py | 0 .../scenario_outlines/__init__.py | 0 .../behave_support/scenarios/__init__.py | 0 .../behave_support/steps/__init__.py | 0 .../behave_support/steps/behave_step_test.py | 38 --- tests/allure_behave/conftest.py | 4 +- tests/allure_behave/defects/__init__.py | 0 tests/allure_behave/defects/issue717_test.py | 73 +++++ .../acceptance/duration/duration_time_test.py | 91 +++++-- .../label/suite/default_suite_test.py | 134 ++++++++-- .../parametrization/parametrization_test.py | 252 +++++++++++------- .../acceptance/step/step_placeholder_test.py | 7 +- ...st_step_with_several_step_inside_thread.py | 76 +++--- tests/allure_robotframework/__init__.py | 0 .../acceptance/__init__.py | 0 .../acceptance/allure_api/__init__.py | 0 .../allure_api/attachment/__init__.py | 0 .../allure_api/attachment/attachment_test.py | 141 ++++++++++ tests/allure_robotframework/conftest.py | 192 +++++++++++++ 45 files changed, 941 insertions(+), 326 deletions(-) delete mode 100644 allure-robotframework/examples/attach/data_attach.rst delete mode 100644 allure-robotframework/examples/attach/file_attach.rst delete mode 100644 allure-robotframework/examples/attach/foreign_library.py delete mode 100644 allure-robotframework/examples/attach/foreign_library_attach.rst delete mode 100644 allure-robotframework/examples/attach/foreign_library_helper.py create mode 100644 allure-robotframework/examples/attachment.rst create mode 100644 tests/allure_behave/__init__.py create mode 100644 tests/allure_behave/acceptance/__init__.py create mode 100644 tests/allure_behave/acceptance/allure_api/__init__.py create mode 100644 tests/allure_behave/acceptance/allure_api/attachment/__init__.py create mode 100644 tests/allure_behave/acceptance/allure_api/description/__init__.py create mode 100644 tests/allure_behave/acceptance/allure_api/labels/__init__.py create mode 100644 tests/allure_behave/acceptance/allure_api/links/__init__.py create mode 100644 tests/allure_behave/acceptance/allure_api/severities/__init__.py create mode 100644 tests/allure_behave/acceptance/allure_api/tags/__init__.py create mode 100644 tests/allure_behave/acceptance/allure_api/testplan/__init__.py create mode 100644 tests/allure_behave/acceptance/behave_support/__init__.py create mode 100644 tests/allure_behave/acceptance/behave_support/background/__init__.py create mode 100644 tests/allure_behave/acceptance/behave_support/behave_cmd/__init__.py create mode 100644 tests/allure_behave/acceptance/behave_support/hooks/__init__.py create mode 100644 tests/allure_behave/acceptance/behave_support/scenario_outlines/__init__.py create mode 100644 tests/allure_behave/acceptance/behave_support/scenarios/__init__.py create mode 100644 tests/allure_behave/acceptance/behave_support/steps/__init__.py create mode 100644 tests/allure_behave/defects/__init__.py create mode 100644 tests/allure_behave/defects/issue717_test.py create mode 100644 tests/allure_robotframework/__init__.py create mode 100644 tests/allure_robotframework/acceptance/__init__.py create mode 100644 tests/allure_robotframework/acceptance/allure_api/__init__.py create mode 100644 tests/allure_robotframework/acceptance/allure_api/attachment/__init__.py create mode 100644 tests/allure_robotframework/acceptance/allure_api/attachment/attachment_test.py create mode 100644 tests/allure_robotframework/conftest.py diff --git a/allure-behave/pyproject.toml b/allure-behave/pyproject.toml index f25e40b6..83bfa75b 100644 --- a/allure-behave/pyproject.toml +++ b/allure-behave/pyproject.toml @@ -1,4 +1,3 @@ [tool.poe.tasks] linter = "flake8 --extend-ignore=A003 ./src" -tests = """behave -f allure_behave.formatter:AllureFormatter -o allure-results - -f pretty ./features""" +tests = """pytest ../tests/allure_behave""" diff --git a/allure-pytest-bdd/pyproject.toml b/allure-pytest-bdd/pyproject.toml index 579ac074..ef264568 100644 --- a/allure-pytest-bdd/pyproject.toml +++ b/allure-pytest-bdd/pyproject.toml @@ -1,3 +1,3 @@ [tool.poe.tasks] -linter = "flake8 ./src ./test" -tests = "pytest --alluredir=allure-results --basetemp=tmp test" +linter = "flake8 ./src" +tests = "pytest ../tests/allure_pytest_bdd" diff --git a/allure-pytest/pyproject.toml b/allure-pytest/pyproject.toml index b7ba99a6..a65620d3 100644 --- a/allure-pytest/pyproject.toml +++ b/allure-pytest/pyproject.toml @@ -1,3 +1,3 @@ [tool.poe.tasks] -linter = "flake8 ./src ./test" -tests = "pytest --alluredir=allure-results --basetemp=tmp test/acceptance test/integration" +linter = "flake8 ./src" +tests = "pytest ../tests/allure_pytest" diff --git a/allure-pytest/src/listener.py b/allure-pytest/src/listener.py index 74bc3324..270a8ca5 100644 --- a/allure-pytest/src/listener.py +++ b/allure-pytest/src/listener.py @@ -78,6 +78,12 @@ def pytest_runtest_protocol(self, item, nextitem): test_result = TestResult(name=item.name, uuid=uuid, start=now(), stop=now()) self.allure_logger.schedule_test(uuid, test_result) yield + uuid = self._cache.pop(item.nodeid) + if uuid: + test_result = self.allure_logger.get_test(uuid) + if test_result.status is None: + test_result.status = Status.SKIPPED + self.allure_logger.close_test(uuid) @pytest.hookimpl(hookwrapper=True) def pytest_runtest_setup(self, item): @@ -218,13 +224,6 @@ def pytest_runtest_makereport(self, item, call): if report.capstderr: self.attach_data(report.capstderr, "stderr", AttachmentType.TEXT, None) - @pytest.hookimpl(hookwrapper=True) - def pytest_runtest_logfinish(self, nodeid, location): - yield - uuid = self._cache.pop(nodeid) - if uuid: - self.allure_logger.close_test(uuid) - @allure_commons.hookimpl def attach_data(self, body, name, attachment_type, extension): self.allure_logger.attach_data(uuid4(), body, name=name, attachment_type=attachment_type, extension=extension) diff --git a/allure-python-commons/src/logger.py b/allure-python-commons/src/logger.py index a3906343..2fed288e 100644 --- a/allure-python-commons/src/logger.py +++ b/allure-python-commons/src/logger.py @@ -75,7 +75,7 @@ def report_container(self, container): @hookimpl def report_attached_file(self, source, file_name): - pass + self.attachments[file_name] = source @hookimpl def report_attached_data(self, body, file_name): diff --git a/allure-robotframework/README.rst b/allure-robotframework/README.rst index a4b8ec64..6f4d5b1d 100644 --- a/allure-robotframework/README.rst +++ b/allure-robotframework/README.rst @@ -25,7 +25,7 @@ Optional argument sets output directory. Example: .. code:: bash - $ robot --listener allure_robotframework;/set/your/path/here ./my_robot_test + $ robot --listener allure_robotframework:/set/your/path/here ./my_robot_test Default output directory is `output/allure`. diff --git a/allure-robotframework/examples/attach/data_attach.rst b/allure-robotframework/examples/attach/data_attach.rst deleted file mode 100644 index 0a98be78..00000000 --- a/allure-robotframework/examples/attach/data_attach.rst +++ /dev/null @@ -1,21 +0,0 @@ - - -.. code:: robotframework - - *** Settings *** - Library AllureLibrary - - *** Test Cases *** - Data Attachment - Attach Hello world - - -.. code:: robotframework - - *** Settings *** - Library AllureLibrary - - *** Test Cases *** - Data Attachment With Name And Type - Attach https://github.com/allure-framework/allure2 https://github.com/allure-framework/allure-python - ... name=links attachment_type=URI_LIST \ No newline at end of file diff --git a/allure-robotframework/examples/attach/file_attach.rst b/allure-robotframework/examples/attach/file_attach.rst deleted file mode 100644 index 038335f7..00000000 --- a/allure-robotframework/examples/attach/file_attach.rst +++ /dev/null @@ -1,8 +0,0 @@ -.. code:: robotframework - - *** Settings *** - Library AllureLibrary - - *** Test Cases *** - File Attachment - Attach File ${SUITE SOURCE} \ No newline at end of file diff --git a/allure-robotframework/examples/attach/foreign_library.py b/allure-robotframework/examples/attach/foreign_library.py deleted file mode 100644 index e1a5d3ef..00000000 --- a/allure-robotframework/examples/attach/foreign_library.py +++ /dev/null @@ -1,15 +0,0 @@ -import os -from tempfile import mkdtemp - - -class ForeignLibrary: - - def capture_page_screenshot(self): - tmp = mkdtemp() - screenshot_path = os.path.join(tmp, 'screenshot.txt') - with open(screenshot_path, 'w+') as screenshot: - screenshot.write("Grab some beer and be happy~(^o^)-c[~]") - return screenshot_path - - -foreign_library = ForeignLibrary diff --git a/allure-robotframework/examples/attach/foreign_library_attach.rst b/allure-robotframework/examples/attach/foreign_library_attach.rst deleted file mode 100644 index d7f3fabc..00000000 --- a/allure-robotframework/examples/attach/foreign_library_attach.rst +++ /dev/null @@ -1,22 +0,0 @@ - -For example, you have a something like a Selenium library. Lets define some stub for it: - -.. literalinclude:: ./foreign_library.py - - -make helper as you project scope library: - -.. literalinclude:: ./foreign_library_helper.py - - -and finally your test writen for foreign library works and makes attachments to allure report: - -.. code:: robotframework - - *** Settings *** - Library ./foreign_library.py - Library ./foreign_library_helper.py - - *** Test Cases *** - Override Library Keyword And Make Allure Attachment - Capture Page Screenshot \ No newline at end of file diff --git a/allure-robotframework/examples/attach/foreign_library_helper.py b/allure-robotframework/examples/attach/foreign_library_helper.py deleted file mode 100644 index a28146ec..00000000 --- a/allure-robotframework/examples/attach/foreign_library_helper.py +++ /dev/null @@ -1,22 +0,0 @@ -import allure -from robot.libraries.BuiltIn import BuiltIn - - -class ForeignLibraryHelper: - ROBOT_LIBRARY_SCOPE = "TEST SUITE" - ROBOT_LISTENER_API_VERSION = 2 - - def __init__(self): - self.ROBOT_LIBRARY_LISTENER = self - - def _start_suite(self, name, attrs): - BuiltIn().set_library_search_order('foreign_library_helper') - - def capture_page_screenshot(self): - helper_library = BuiltIn().get_library_instance('foreign_library') - path = helper_library.capture_page_screenshot() - allure.attach.file(path, name="screenshot", attachment_type=allure.attachment_type.TEXT) - return path - - -foreign_library_helper = ForeignLibraryHelper diff --git a/allure-robotframework/examples/attachment.rst b/allure-robotframework/examples/attachment.rst new file mode 100644 index 00000000..10ace0eb --- /dev/null +++ b/allure-robotframework/examples/attachment.rst @@ -0,0 +1,138 @@ +==================================== +Attachments in allure_robotframework +==================================== + +You can attach data and files to a test result with the :code:`Attach` and +:code:`Attach File` keywords. + +--------------- +Data attachment +--------------- + +Use the :code:`Attach` to attach some textual data to your test: + +.. code:: robotframework + :name: data-attachment-default + + *** Settings *** + Library AllureLibrary + + *** Variables *** + ${log_message} This attachment was created using the allure_robotframework + ... library + + *** Test Cases *** + Data Attachment + Attach ${log_message} + +You can provide a name and a type of your attachments: + +.. code:: robotframework + :name: data-attachment-name-type + + *** Settings *** + Library AllureLibrary + + *** Variables *** + ${links} https://github.com/allure-framework/allure2 + ... https://github.com/allure-framework/allure-python + + *** Test Cases *** + Data Attachment + Attach ${links} + ... name=links attachment_type=URI_LIST + + +--------------- +File attachment +--------------- + +Use the :code:`Attach File` keyword to attach some file: + +.. code:: robotframework + :name: file-attachment-default + + *** Settings *** + Library AllureLibrary + + *** Test Cases *** + File Attachment + Attach File ./my_file.txt + +It's possible to specify a name and a type of the attachment: + +.. code:: robotframework + :name: file-attachment-name-type + + *** Settings *** + Library AllureLibrary + + *** Test Cases *** + File Attachment + Attach File ./my_file.txt + ... name=my-file attachment_type=TEXT + + +-------------------------------------------------- +Attachment of files created by 3rd party libraries +-------------------------------------------------- + +If you want to automatically attach files, created by some other library, you +can use the trick, described below. + +Lets say, we want to automatically attach screenshots, created by the +`SeleniumLibrary's Capture Page Screenshot`_ keyword. To achieve that, lets +create a wrapper over our library of interest: + +selenium_wrapper.py: +^^^^^^^^^^^^^^^^^^^^ +.. code:: python + :name: selenium-wrapper + + import allure + from robot.libraries.BuiltIn import BuiltIn + + class SeleniumWrapper: + ROBOT_LIBRARY_SCOPE = "TEST SUITE" + ROBOT_LISTENER_API_VERSION = 2 + + def __init__(self): + self.ROBOT_LIBRARY_LISTENER = self + + def _start_suite(self, name, attrs): + BuiltIn().set_library_search_order(__name__) + + def capture_page_screenshot(self, *args, **kwargs): + target_lib = BuiltIn().get_library_instance('SeleniumLibrary') + path = target_lib.capture_page_screenshot(*args, **kwargs) + allure.attach.file( + path, + name="page", + attachment_type=allure.attachment_type.JPG + ) + return path + + selenium_wrapper = SeleniumWrapper + +The wrapper sets itself as the first library in the library resolution order of +the Robot Framework. It uses the SeleniumLibrary under the hood, attaching the +output file to the allure report. + +Declare both the library and the wrapper in your .robot file: + +.. code:: robotframework + :name: selenium-suite + + *** Settings *** + Library SeleniumLibrary + Library ./selenium_wrapper.py + + *** Test Cases *** + Automatic Screenshot Attachment + Open Browser https://localhost:443 Chrome + Capture Page Screenshot + [Teardown] Close Browser + +All screenshots are now automatically attached to your allure report. + +.. _SeleniumLibrary's Capture Page Screenshot: https://robotframework.org/SeleniumLibrary/SeleniumLibrary.html#Capture%20Page%20Screenshot diff --git a/allure-robotframework/pyproject.toml b/allure-robotframework/pyproject.toml index 7b805fda..41bf95b1 100644 --- a/allure-robotframework/pyproject.toml +++ b/allure-robotframework/pyproject.toml @@ -1,8 +1,5 @@ [tool.poe.tasks] linter = "flake8 ./src ./test" tests = { shell = """python -m doctest ./src/listener/utils.py && - robot --log NONE --report NONE --output NONE \ - --extension robot --loglevel DEBUG \ - --listener allure_robotframework:allure-results \ - --outputdir allure-results ./test + pytest ../tests/allure_robotframework """ } diff --git a/tests/allure_behave/__init__.py b/tests/allure_behave/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/__init__.py b/tests/allure_behave/acceptance/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/allure_api/__init__.py b/tests/allure_behave/acceptance/allure_api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/allure_api/attachment/__init__.py b/tests/allure_behave/acceptance/allure_api/attachment/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/allure_api/description/__init__.py b/tests/allure_behave/acceptance/allure_api/description/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/allure_api/labels/__init__.py b/tests/allure_behave/acceptance/allure_api/labels/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/allure_api/links/__init__.py b/tests/allure_behave/acceptance/allure_api/links/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/allure_api/severities/__init__.py b/tests/allure_behave/acceptance/allure_api/severities/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/allure_api/tags/__init__.py b/tests/allure_behave/acceptance/allure_api/tags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/allure_api/testplan/__init__.py b/tests/allure_behave/acceptance/allure_api/testplan/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/behave_support/__init__.py b/tests/allure_behave/acceptance/behave_support/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/behave_support/background/__init__.py b/tests/allure_behave/acceptance/behave_support/background/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/behave_support/behave_cmd/__init__.py b/tests/allure_behave/acceptance/behave_support/behave_cmd/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/behave_support/hooks/__init__.py b/tests/allure_behave/acceptance/behave_support/hooks/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/behave_support/scenario_outlines/__init__.py b/tests/allure_behave/acceptance/behave_support/scenario_outlines/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/behave_support/scenarios/__init__.py b/tests/allure_behave/acceptance/behave_support/scenarios/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/behave_support/steps/__init__.py b/tests/allure_behave/acceptance/behave_support/steps/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/acceptance/behave_support/steps/behave_step_test.py b/tests/allure_behave/acceptance/behave_support/steps/behave_step_test.py index 1185612b..0cae4b70 100644 --- a/tests/allure_behave/acceptance/behave_support/steps/behave_step_test.py +++ b/tests/allure_behave/acceptance/behave_support/steps/behave_step_test.py @@ -132,41 +132,3 @@ def test_step_table_data(docstring:str, behave_runner: AllureBehaveRunner): ) ) ) - - -@allure.issue("https://github.com/allure-framework/allure-python/issues/717") -def test_step_table_data_escaping( - docstring:str, - behave_runner: AllureBehaveRunner -): - """ - Feature: Bheave step support - Scenario: Csv special symbols in step's table data - Given a step table data with special symbols - | Item A | Item B | - | Item "1", Item 2 | Item 3," Item 4 | - """ - - behave_runner.run_behave( - features=[docstring], - steps=["given('a step table data with special symbols')(lambda _:0)"] - ) - - assert_that( - behave_runner.allure_results, - has_test_case( - "Csv special symbols in step's table data", - with_status("passed"), - has_step( - "Given a step table data with special symbols", - has_attachment_with_content( - behave_runner.allure_results.attachments, - csv_equivalent([ - ["Item A", "Item B"], - ["Item \"1\", Item 2", "Item 3,\" Item 4"] - ]), - "text/csv" - ) - ) - ) - ) diff --git a/tests/allure_behave/conftest.py b/tests/allure_behave/conftest.py index 2a95b6e0..2aa1b4be 100644 --- a/tests/allure_behave/conftest.py +++ b/tests/allure_behave/conftest.py @@ -140,8 +140,8 @@ def run_behave( environment_path (str|Path): a path to the environment.py file. cli_args (Sequence[str]): a CLI arguments passed to behave. - The results of the run could be accessed through the - :code:`allure_results` attribute. + The result of the run is accessible through the :code:`allure_results` + attribute. """ path_to_fake = "allure_behave.formatter.AllureFileLogger" diff --git a/tests/allure_behave/defects/__init__.py b/tests/allure_behave/defects/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_behave/defects/issue717_test.py b/tests/allure_behave/defects/issue717_test.py new file mode 100644 index 00000000..4e928e68 --- /dev/null +++ b/tests/allure_behave/defects/issue717_test.py @@ -0,0 +1,73 @@ +import allure +import pytest + +from allure_behave.utils import step_table +from allure_commons_test.content import csv_equivalent +from hamcrest import assert_that + +class RowStub: + def __init__(self, cells): + self.cells = cells + def __iter__(self): + return iter(self.cells) + + +class TableStub: + def __init__(self, headings, rows): + self.headings = headings + self.rows = [RowStub(cells) for cells in rows] + + +class StepStub: + def __init__(self, content): + headings, *rows = content + table = TableStub(headings, rows) + self.table = table + + +class CsvTestData: + def __init__(self, content): + self.step = StepStub(content) + self.content = content + + +@pytest.fixture(params=[ + pytest.param( + [ + ["a"], + [","] + ], + id="comma" + ), + pytest.param( + [ + ["a"], + ["\""] + ], + id="quote" + ), + pytest.param( + [ + ["a", "b"], + ["1,2", "3,4"] + ], + id="2c-commas" + ), + pytest.param( + [ + ["a", "b"], + ["\"1\",2", "3\",4"] + ], + id="mix" + ) +]) +def csv_testdata(request): + yield CsvTestData(request.param) + + +@allure.issue("717") +def test_step_table_data_escaping(csv_testdata): + assert_that( + step_table(csv_testdata.step), + csv_equivalent(csv_testdata.content) + ) diff --git a/tests/allure_pytest/acceptance/duration/duration_time_test.py b/tests/allure_pytest/acceptance/duration/duration_time_test.py index e5afc9d0..68fd51e1 100644 --- a/tests/allure_pytest/acceptance/duration/duration_time_test.py +++ b/tests/allure_pytest/acceptance/duration/duration_time_test.py @@ -2,48 +2,52 @@ import pytest from hamcrest import assert_that, has_entry, greater_than, all_of from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status from allure_commons.utils import now snippets = [ "pass", - "assert False", - "raise(RuntimeError())", - "pytest.skip()", - "pytest.fail()", - "pytest.xfail()", - pytest.param("pytest.exit('msg')", marks=pytest.mark.skip) + pytest.param("assert False", id="assert-fail"), + pytest.param("raise RuntimeError()", id="break"), + pytest.param("pytest.skip()", id="skip"), + pytest.param("pytest.fail()", id="pytest-fail"), + pytest.param("pytest.xfail()", id="xfail"), + pytest.param("pytest.exit('msg')", id="exit"), ] -@pytest.mark.parametrize("snipped", snippets) -def test_duration(allured_testdir, snipped): +@pytest.mark.parametrize("snippet", snippets) +def test_duration(allured_testdir, snippet): allured_testdir.testdir.makepyfile(f""" def test_duration_example(): - {snipped} + {snippet} """) timestamp = now() allured_testdir.run_with_allure() - assert_that(allured_testdir.allure_report, - has_test_case("test_duration_example", - all_of( - has_entry("start", greater_than(timestamp)), - has_entry("stop", greater_than(timestamp)) - )) - ) + assert_that( + allured_testdir.allure_report, + has_test_case( + "test_duration_example", + all_of( + has_entry("start", greater_than(timestamp)), + has_entry("stop", greater_than(timestamp)) + ) + ) + ) @allure.issue("244") -@pytest.mark.parametrize("snipped", snippets) -def test_with_fixture_duration(allured_testdir, snipped): +@pytest.mark.parametrize("snippet", snippets) +def test_with_fixture_duration(allured_testdir, snippet): allured_testdir.testdir.makepyfile(f""" import pytest @pytest.fixture def fixture(): - {snipped} + {snippet} def test_with_fixture_duration_example(fixture): pass @@ -52,25 +56,28 @@ def test_with_fixture_duration_example(fixture): timestamp = now() allured_testdir.run_with_allure() - assert_that(allured_testdir.allure_report, - has_test_case("test_with_fixture_duration_example", - all_of( - has_entry("start", greater_than(timestamp)), - has_entry("stop", greater_than(timestamp)) - )) - ) + assert_that( + allured_testdir.allure_report, + has_test_case( + "test_with_fixture_duration_example", + all_of( + has_entry("start", greater_than(timestamp)), + has_entry("stop", greater_than(timestamp)) + ) + ) + ) @allure.issue("244") -@pytest.mark.parametrize("snipped", snippets) -def test_with_fixture_finalizer_duration(allured_testdir, snipped): +@pytest.mark.parametrize("snippet", snippets) +def test_with_fixture_finalizer_duration(allured_testdir, snippet): allured_testdir.testdir.makepyfile(f""" import pytest @pytest.fixture def fixture(request): def finalizer(): - {snipped} + {snippet} request.addfinalizef(finalizer) def test_with_fixture_finalizer_duration(fixture): @@ -87,3 +94,29 @@ def test_with_fixture_finalizer_duration(fixture): has_entry("stop", greater_than(timestamp)) )) ) + + +def test_test_skipped_if_fixture_exits(allured_testdir): + """Test should be market as skipped: pytest reports it as 'not run'""" + + allured_testdir.testdir.makepyfile(f""" + import pytest + + @pytest.fixture + def fixture(): + pytest.exit("Reason") + + def test_with_fixture_duration_example(fixture): + pass + """) + + timestamp = now() + allured_testdir.run_with_allure() + + assert_that( + allured_testdir.allure_report, + has_test_case( + "test_with_fixture_duration_example", + with_status("skipped") + ) + ) diff --git a/tests/allure_pytest/acceptance/label/suite/default_suite_test.py b/tests/allure_pytest/acceptance/label/suite/default_suite_test.py index daf1d588..63260189 100644 --- a/tests/allure_pytest/acceptance/label/suite/default_suite_test.py +++ b/tests/allure_pytest/acceptance/label/suite/default_suite_test.py @@ -1,4 +1,5 @@ -import pytest +from doctest import script_from_examples +from tests.conftest import AlluredTestdir from hamcrest import assert_that, anything, not_ from allure_commons_test.report import has_test_case from allure_commons_test.label import has_parent_suite @@ -6,24 +7,24 @@ from allure_commons_test.label import has_sub_suite -@pytest.mark.skip -def test_default_suite(executed_docstring_source): +def test_default_suites_no_parent_module(executed_docstring_source): """ >>> def test_default_suite_example(): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_default_suite_example", - has_parent_suite(anything()), # path to testdir - has_suite("test_default_suite"), # created file name - not_(has_sub_suite(anything())) - ) - ) + assert_that( + executed_docstring_source.allure_report, + has_test_case( + "test_default_suite_example", + not_(has_parent_suite(anything())), + has_suite("test_default_suites_no_parent_module"), + not_(has_sub_suite(anything())) + ) + ) -@pytest.mark.skip -def test_default_class_suite(executed_docstring_source): +def test_default_suites_class_no_parent_module(executed_docstring_source): """ >>> class TestSuiteClass: ... def test_default_class_suite_example(self): @@ -31,10 +32,105 @@ def test_default_class_suite(executed_docstring_source): """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_default_class_suite_example", - has_parent_suite(anything()), # path to testdir - has_suite("test_default_class_suite"), # created file name - has_sub_suite("TestSuiteClass") - ) - ) + assert_that( + executed_docstring_source.allure_report, + has_test_case( + "test_default_class_suite_example", + not_(has_parent_suite(anything())), + has_suite("test_default_suites_class_no_parent_module"), + has_sub_suite("TestSuiteClass") + ) + ) + + +def test_default_suites_with_class_and_parent_module( + docstring: str, + allured_testdir: AlluredTestdir +): + """ + >>> class TestSuiteClass: + ... def test_default_class_suite_example(self): + ... pass + + """ + + content = script_from_examples(docstring) + allured_testdir.testdir.makepyfile( + **{ + "parent_module/test_default_suites_with_parent_module.py": content + } + ) + allured_testdir.run_with_allure() + + assert_that( + allured_testdir.allure_report, + has_test_case( + "test_default_class_suite_example", + has_parent_suite("parent_module"), + has_suite("test_default_suites_with_parent_module"), + has_sub_suite("TestSuiteClass") + ) + ) + + +def test_default_suites_with_parent_module( + docstring: str, + allured_testdir: AlluredTestdir +): + """ + >>> def test_default_class_suite_example(self): + ... pass + + """ + + content = script_from_examples(docstring) + module = "test_default_suites_with_class_and_parent_module" + filename = module + ".py" + fullname = "parent_module/" + filename + allured_testdir.testdir.makefile( + ".py", + **{fullname: content } + ) + allured_testdir.run_with_allure() + + assert_that( + allured_testdir.allure_report, + has_test_case( + "test_default_class_suite_example", + has_parent_suite("parent_module"), + has_suite(module), + not_(has_sub_suite(anything())) + ) + ) + + +def test_default_suites_with_class_and_parent_module( + docstring: str, + allured_testdir: AlluredTestdir +): + """ + >>> class TestSuiteClass: + ... def test_default_class_suite_example(self): + ... pass + + """ + + content = script_from_examples(docstring) + module = "test_default_suites_with_class_and_parent_module" + filename = module + ".py" + fullname = "parent_module/" + filename + allured_testdir.testdir.makefile( + ".py", + **{fullname: content } + ) + allured_testdir.run_with_allure() + + assert_that( + allured_testdir.allure_report, + has_test_case( + "test_default_class_suite_example", + has_parent_suite("parent_module"), + has_suite(module), + has_sub_suite("TestSuiteClass") + ) + ) diff --git a/tests/allure_pytest/acceptance/parametrization/parametrization_test.py b/tests/allure_pytest/acceptance/parametrization/parametrization_test.py index d03c14e5..8ff611df 100644 --- a/tests/allure_pytest/acceptance/parametrization/parametrization_test.py +++ b/tests/allure_pytest/acceptance/parametrization/parametrization_test.py @@ -1,7 +1,9 @@ import pytest -from hamcrest import assert_that, has_entry, ends_with +from hamcrest import assert_that, has_entry, ends_with, all_of from allure_commons_test.report import has_test_case -from allure_commons_test.result import has_parameter, with_excluded, with_mode +from allure_commons_test.result import has_parameter +from allure_commons_test.result import with_excluded +from allure_commons_test.result import with_mode def params_name(request): @@ -10,88 +12,126 @@ def params_name(request): return name -@pytest.mark.parametrize("param", [True, False]) -def test_parametrization(executed_docstring_source, param): +def test_parametrization(executed_docstring_source): """ >>> import pytest - >>> @pytest.mark.parametrize("param", [True, False]) - ... def test_parametrization_example(param): + >>> @pytest.mark.parametrize("n", [1, 2]) + ... def test_parametrization_example(n): ... assert param """ - assert_that(executed_docstring_source.allure_report, - has_test_case(f"test_parametrization_example[{param}]", - has_parameter("param", str(param)) - ) - ) - - -@pytest.mark.xfail() -@pytest.mark.parametrize("param", [True, False], ids=["pass", "fail"]) -def test_parametrization_with_ids(executed_docstring_source, param): + assert_that( + executed_docstring_source.allure_report, + all_of( + has_test_case( + "test_parametrization_example[1]", + has_parameter("n", "1") + ), + has_test_case( + "test_parametrization_example[2]", + has_parameter("n", "2") + ) + ) + ) + + +def test_parametrization_with_ids(executed_docstring_source): """ >>> import pytest - >>> @pytest.mark.parametrize("param", [True, False], ids=["pass", "fail"]) - ... def test_parametrization_with_ids_example(param): - ... assert param + >>> @pytest.mark.parametrize("v", [1, 2], ids=["a", "b"]) + ... def test_parametrization_with_ids_example(v): + ... pass """ - param_name = "pass" if param else "fail" - assert_that(executed_docstring_source.allure_report, - has_test_case( - f"test_parametrization_with_ids_example[{param_name}]", - has_parameter(param_name, str(param)) - ) - ) - - -@pytest.mark.parametrize("param1", [True, False]) -@pytest.mark.parametrize("param2", [True, True]) -def test_parametrization_many_decorators(executed_docstring_source, request, param1, param2): + assert_that( + executed_docstring_source.allure_report, + all_of( + has_test_case( + f"test_parametrization_with_ids_example[a]", + has_parameter("v", "1") + ), + has_test_case( + f"test_parametrization_with_ids_example[b]", + has_parameter("v", "2") + ) + ) + ) + + +def test_parametrization_many_decorators(executed_docstring_source): """ >>> import pytest - >>> @pytest.mark.parametrize("param1", [True, False]) - ... @pytest.mark.parametrize("param2", [True, True]) - ... def test_parametrization_many_decorators_example(param1, param2): + >>> @pytest.mark.parametrize("s", ["a", "b"]) + ... @pytest.mark.parametrize("n", [1, 2]) + ... def test_parametrization_many_decorators_example(n, s): ... pass """ - test_name = f"test_parametrization_many_decorators_example[{params_name(request)}]" - - assert_that(executed_docstring_source.allure_report, - has_test_case(test_name, - has_parameter("param1", str(param1)), - has_parameter("param2", str(param2)) - - ) - ) - - -@pytest.mark.xfail() -@pytest.mark.parametrize("param1", [True, False], ids=["pass", "fail"]) -@pytest.mark.parametrize("param2", [True, True]) -def test_parametrization_many_decorators_with_partial_ids(executed_docstring_source, request, param1, param2): + assert_that( + executed_docstring_source.allure_report, + all_of( + has_test_case( + "test_parametrization_many_decorators_example[1-a]", + has_parameter("n", "1"), + has_parameter("s", "'a'") + ), + has_test_case( + "test_parametrization_many_decorators_example[1-b]", + has_parameter("n", "1"), + has_parameter("s", "'b'") + ), + has_test_case( + "test_parametrization_many_decorators_example[2-a]", + has_parameter("n", "2"), + has_parameter("s", "'a'") + ), + has_test_case( + "test_parametrization_many_decorators_example[2-b]", + has_parameter("n", "2"), + has_parameter("s", "'b'") + ) + ) + ) + + +def test_parametrization_decorators_with_partial_ids(executed_docstring_source): """ >>> import pytest - >>> @pytest.mark.parametrize("param1", [True, False], ids=["first_pass", "first_fail"]) - ... @pytest.mark.parametrize("param2", [True, True]) - ... def test_parametrization_many_decorators_with_partial_ids_example(param1, param2): + >>> @pytest.mark.parametrize("s", ["a", "b"], ids=["A", "B"]) + ... @pytest.mark.parametrize("n", [1, 2]) + ... def test_two_marks_one_with_ids(n, s): ... pass """ - test_name = f"test_parametrization_many_decorators_with_partial_ids_example[{params_name(request)}]" - - assert_that(executed_docstring_source.allure_report, - has_test_case(test_name, - has_parameter("pass" if param1 else "fail", str(param1)), - has_parameter("param2", str(param2)) - - ) - ) + assert_that( + executed_docstring_source.allure_report, + all_of( + has_test_case( + "test_two_marks_one_with_ids[1-A]", + has_parameter("n", "1"), + has_parameter("s", "'a'") + ), + has_test_case( + "test_two_marks_one_with_ids[1-B]", + has_parameter("n", "1"), + has_parameter("s", "'b'") + ), + has_test_case( + "test_two_marks_one_with_ids[2-A]", + has_parameter("n", "2"), + has_parameter("s", "'a'") + ), + has_test_case( + "test_two_marks_one_with_ids[2-B]", + has_parameter("n", "2"), + has_parameter("s", "'b'") + ) + ) + ) def test_dynamic_parameter_add(executed_docstring_source): @@ -101,11 +141,13 @@ def test_dynamic_parameter_add(executed_docstring_source): >>> def test_parameter_add(): ... allure.dynamic.parameter("param1", "param-value") """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_parameter_add", - has_parameter("param1", "'param-value'") - ) - ) + assert_that( + executed_docstring_source.allure_report, + has_test_case( + "test_parameter_add", + has_parameter("param1", "'param-value'") + ) + ) def test_dynamic_parameter_excluded(executed_docstring_source): @@ -115,12 +157,18 @@ def test_dynamic_parameter_excluded(executed_docstring_source): >>> def test_parameter_excluded(): ... allure.dynamic.parameter("param1", "param-value", excluded=True) """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_parameter_excluded", - has_parameter("param1", "'param-value'", - with_excluded()) - ) - ) + + assert_that( + executed_docstring_source.allure_report, + has_test_case( + "test_parameter_excluded", + has_parameter( + "param1", + "'param-value'", + with_excluded() + ) + ) + ) def test_dynamic_parameter_mode(executed_docstring_source): @@ -130,12 +178,18 @@ def test_dynamic_parameter_mode(executed_docstring_source): >>> def test_parameter_mode(): ... allure.dynamic.parameter("param1", "param-value", mode=allure.parameter_mode.MASKED) """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_parameter_mode", - has_parameter("param1", "'param-value'", - with_mode('masked')) - ) - ) + + assert_that( + executed_docstring_source.allure_report, + has_test_case( + "test_parameter_mode", + has_parameter( + "param1", + "'param-value'", + with_mode('masked') + ) + ) + ) def test_dynamic_parameter_override(executed_docstring_source): @@ -147,11 +201,13 @@ def test_dynamic_parameter_override(executed_docstring_source): ... def test_parameter_override(param1): ... allure.dynamic.parameter("param1", "readable-value") """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_parameter_override[param-id]", - has_parameter("param1", "'readable-value'") - ) - ) + assert_that( + executed_docstring_source.allure_report, + has_test_case( + "test_parameter_override[param-id]", + has_parameter("param1", "'readable-value'") + ) + ) def test_dynamic_parameter_override_from_fixture(executed_docstring_source): @@ -168,11 +224,13 @@ def test_dynamic_parameter_override_from_fixture(executed_docstring_source): ... def test_parameter_override_from_fixture(fixt, param1): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_parameter_override_from_fixture[param-id]", - has_parameter("param1", "'readable-value'") - ) - ) + assert_that( + executed_docstring_source.allure_report, + has_test_case( + "test_parameter_override_from_fixture[param-id]", + has_parameter("param1", "'readable-value'") + ) + ) def test_fullname_with_braces(executed_docstring_source): @@ -185,9 +243,15 @@ def test_fullname_with_braces(executed_docstring_source): ... def test_with_braces(self, param1): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_with_braces[qwe][]", - has_entry('fullName', ends_with(".TestClass#test_with_braces")), - has_parameter("param1", "'qwe]['") - ) - ) + + assert_that( + executed_docstring_source.allure_report, + has_test_case( + "test_with_braces[qwe][]", + has_entry( + 'fullName', + ends_with(".TestClass#test_with_braces") + ), + has_parameter("param1", "'qwe]['") + ) + ) diff --git a/tests/allure_pytest/acceptance/step/step_placeholder_test.py b/tests/allure_pytest/acceptance/step/step_placeholder_test.py index fdb9c9f8..2ed86886 100644 --- a/tests/allure_pytest/acceptance/step/step_placeholder_test.py +++ b/tests/allure_pytest/acceptance/step/step_placeholder_test.py @@ -4,6 +4,7 @@ from allure_commons_test.report import has_test_case from allure_commons_test.result import has_step from allure_commons_test.result import has_status_details +from allure_commons_test.result import with_status from allure_commons_test.result import with_message_contains @@ -37,7 +38,6 @@ def test_class_method_as_step(executed_docstring_path): ) -@pytest.mark.skip() def test_args_less_than_placeholders(executed_docstring_source): """ >>> import allure @@ -54,6 +54,9 @@ def test_args_less_than_placeholders(executed_docstring_source): executed_docstring_source.allure_report, has_test_case( "test_args_less_than_placeholders_example", - has_status_details(with_message_contains("IndexError: tuple index out of range")) + with_status("broken"), + has_status_details( + with_message_contains("IndexError") + ) ) ) diff --git a/tests/allure_pytest/acceptance/step/test_step_with_several_step_inside_thread.py b/tests/allure_pytest/acceptance/step/test_step_with_several_step_inside_thread.py index 5fdc56fd..ff90f16d 100644 --- a/tests/allure_pytest/acceptance/step/test_step_with_several_step_inside_thread.py +++ b/tests/allure_pytest/acceptance/step/test_step_with_several_step_inside_thread.py @@ -35,40 +35,46 @@ def test_thread(): ) -def test_step_with_reused_threads(allured_testdir): - allured_testdir.testdir.makepyfile( - """ - from concurrent.futures import ThreadPoolExecutor - - import allure - import random - from time import sleep - - @allure.step("thread {x}") - def parallel_step(x=1): - sleep(random.randint(0, 3)) - - def test_thread(): - with ThreadPoolExecutor(max_workers=2) as executor: - executor.map(parallel_step, range(1, 4)) - with allure.step("Reuse previous threads"): - with ThreadPoolExecutor(max_workers=2) as executor: - executor.map(parallel_step, range(1, 4)) - +def test_step_with_reused_threads(executed_docstring_source): + """ + >>> from concurrent.futures import ThreadPoolExecutor + >>> from threading import Event + >>> from random import shuffle + >>> from time import sleep + >>> import allure + + >>> def parallel_step(data): + ... event, index = data + ... with allure.step(f"thread {index}"): + ... event.wait() + + >>> def __execute_randomly(executor): + ... events = [Event() for i in range(1, 4)] + ... executor.map(parallel_step, zip(events, range(1, 4))) + ... shuffle(events) + ... for e in events: + ... e.set() + + >>> def test_thread(): + ... with ThreadPoolExecutor(max_workers=2) as executor: + ... __execute_randomly(executor) + ... with allure.step("Reuse previous threads"): + ... with ThreadPoolExecutor(max_workers=2) as executor: + ... __execute_randomly(executor) """ - ) - - allured_testdir.run_with_allure() - assert_that(allured_testdir.allure_report, - has_test_case("test_thread", - has_step("thread 1"), - has_step("thread 2"), - has_step("thread 3"), - has_step("Reuse previous threads", - has_step("thread 1"), - has_step("thread 2"), - has_step("thread 3"), - ), - ) - ) + assert_that( + executed_docstring_source.allure_report, + has_test_case( + "test_thread", + has_step("thread 1"), + has_step("thread 2"), + has_step("thread 3"), + has_step( + "Reuse previous threads", + has_step("thread 1"), + has_step("thread 2"), + has_step("thread 3"), + ) + ) + ) diff --git a/tests/allure_robotframework/__init__.py b/tests/allure_robotframework/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_robotframework/acceptance/__init__.py b/tests/allure_robotframework/acceptance/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_robotframework/acceptance/allure_api/__init__.py b/tests/allure_robotframework/acceptance/allure_api/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_robotframework/acceptance/allure_api/attachment/__init__.py b/tests/allure_robotframework/acceptance/allure_api/attachment/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_robotframework/acceptance/allure_api/attachment/attachment_test.py b/tests/allure_robotframework/acceptance/allure_api/attachment/attachment_test.py new file mode 100644 index 00000000..505a7b1e --- /dev/null +++ b/tests/allure_robotframework/acceptance/allure_api/attachment/attachment_test.py @@ -0,0 +1,141 @@ +""" ./allure-robotframework/examples/attachment.rst """ + +from hamcrest import assert_that, equal_to +from tests.allure_robotframework.conftest import AllureRobotRunner +from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_attachment_with_content +from allure_commons_test.result import has_step +from allure_commons_test.result import with_status + +def test_data_attachment_with_default_name_and_type( + robot_runner: AllureRobotRunner +): + robot_runner.run_robotframework( + suite_rst_ids={"attach-data.robot": "data-attachment-default"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Attach-Data.Data Attachment", + has_step( + "AllureLibrary.Attach", + has_attachment_with_content( + robot_runner.allure_results.attachments, + equal_to( + "This attachment was created using the " + "allure_robotframework library" + ) + ) + ) + ) + ) + + +def test_data_attachment_with_name_and_type( + robot_runner: AllureRobotRunner +): + robot_runner.run_robotframework( + suite_rst_ids={"attach-data.robot": "data-attachment-name-type"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Attach-Data.Data Attachment", + has_step( + "AllureLibrary.Attach", + has_attachment_with_content( + robot_runner.allure_results.attachments, + equal_to( + "https://github.com/allure-framework/allure2 " + "https://github.com/allure-framework/allure-python" + ), + "text/uri-list", + "links" + ) + ) + ) + ) + + +def test_file_attachment_with_default_name_and_type( + robot_runner: AllureRobotRunner +): + robot_runner.run_robotframework( + suite_rst_ids={"attach-file.robot": "file-attachment-default"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Attach-File.File Attachment", + has_step( + "AllureLibrary.Attach File", + has_attachment_with_content( + robot_runner.allure_results.attachments, + equal_to("./my_file.txt") + ) + ) + ) + ) + + +def test_file_attachment_with_name_and_type( + robot_runner: AllureRobotRunner +): + robot_runner.run_robotframework( + suite_rst_ids={"attach-file.robot": "file-attachment-name-type"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Attach-File.File Attachment", + has_step( + "AllureLibrary.Attach File", + has_attachment_with_content( + robot_runner.allure_results.attachments, + equal_to("./my_file.txt"), + "text/plain", + "my-file" + ) + ) + ) + ) + + +def test_autoattach_wrapper(robot_runner: AllureRobotRunner): + robot_runner.monkeypatch.syspath_prepend(".") + + robot_runner.run_robotframework( + suite_rst_ids={"selenium-wrapper.robot": "selenium-suite"}, + library_literals={ + "SeleniumLibrary": ( + """ + def open_browser(*_):pass + def capture_page_screenshot(*_):return "./screenshot.jpg" + def close_browser(*_):pass + """ + ) + }, + library_rst_ids={"selenium_wrapper": "selenium-wrapper"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Selenium-Wrapper.Automatic Screenshot Attachment", + with_status("passed"), + has_step( + "selenium_wrapper.Capture Page Screenshot", + with_status("passed"), + has_attachment_with_content( + robot_runner.allure_results.attachments, + equal_to("./screenshot.jpg"), + "image/jpg", + "page" + ) + ) + ) + ) \ No newline at end of file diff --git a/tests/allure_robotframework/conftest.py b/tests/allure_robotframework/conftest.py new file mode 100644 index 00000000..001c1c83 --- /dev/null +++ b/tests/allure_robotframework/conftest.py @@ -0,0 +1,192 @@ +import shutil +import robot +import json +from pathlib import Path +from pytest import FixtureRequest, Pytester, MonkeyPatch, fixture +from tests.conftest import fake_logger +from typing import Sequence, Mapping +from tests.conftest import RstExampleTable +from allure_robotframework import allure_robotframework + +class AllureRobotRunner: + LOGGER_PATH = "allure_robotframework.robot_listener.AllureFileLogger" + + def __init__( + self, + request: FixtureRequest, + pytester: Pytester, + monkeypatch: MonkeyPatch + ) -> None: + self.request = request + self.pytester = pytester + self.monkeypatch = monkeypatch + self.allure_results = None + + def run_robotframework( + self, + suite_paths: Sequence[str|Path] = None, + suite_literals: Mapping[str, str] = None, + suite_rst_ids: Sequence[str] = None, + library_paths: Sequence[str|Path] = None, + library_literals: Mapping[str, str] = None, + library_rst_ids: Sequence[str] = None, + testplan: Mapping[str, any] = None, + testplan_path: str|Path = None, + testplan_rst_id: str = None, + options: Mapping[str, any] = None + ) -> None: + """Runs the robotframework against an example. + + The example consists of suite(s), i.e., .robot files, libraries, + i.e., .py files and, optionaly, a testplan. All types of files can be + specified either as paths or as a mapping from a file name to its content + or as a mapping from a file name to an ID in the associated .rst + document. + + Arguments: + suite_paths (Sequence[str|Path]): a sequence of path-like objects + pointing to .robot files. + suite_literals (Mapping[str, str]): a mapping from a file name to + the content of a .robot file (.robot extension can be omitted). + suite_rst_ids (Mapping[str, str]): a mapping from a file name to an + ID of a .robot file content from the .rst document, associated + with current node. + library_paths (Sequence[str|Path]): a sequence of path-like objects + pointing to .py library files. + library_literals (Mapping[str, str]): a mapping from a file name to + the content of a .py library file (.py extension can be + omitted). + library_rst_ids (Mapping[str, str]): a mapping from a file name to + an IDs of a .py library file content from the .rst document, + associated with current node. + testplan (Mapping[str, any]): a testplan content. + testplan_path (str|Path): a path to a testplan. + testplan_rst_id (str): a testplan ID from the .rst document, + associated with current node. + options (Mapping[str, any]): robot framework options. + + The result of the run is accessible through the :code:`allure_results` + attribute. + """ + + suites, testplan_path = self.__prepare_example( + suite_literals=suite_literals, + suite_paths=suite_paths, + suite_rst_ids=suite_rst_ids, + library_literals=library_literals, + library_paths=library_paths, + library_rst_ids=library_rst_ids, + testplan=testplan, + testplan_path=testplan_path, + testplan_rst_id=testplan_rst_id, + ) + if testplan_path: + self.__run_with_testplan(suites, testplan_path, options) + else: + self.__run(suites, options) + + def __resolve_options(self, options): + return { + ** { + "listener": allure_robotframework(None), + "log": None, + "loglevel": "DEBUG", + "report": None, + "output": None, + "extension": "robot", + "outputdir": str(self.pytester.path) + }, + ** (options or {}) + } + + def __prepare_example( + self, + suite_literals, + suite_paths, + suite_rst_ids, + library_literals, + library_paths, + library_rst_ids, + testplan, + testplan_path, + testplan_rst_id + ): + suites = self.__make_files_from_literals(".robot", suite_literals) + suites.extend( + self.__copy_files(suite_paths) + ) + suites.extend( + self.__make_all_files_from_rst(".robot", suite_rst_ids) + ) + self.__make_files_from_literals(".py", library_literals) + self.__copy_files(library_paths) + self.__make_all_files_from_rst(".py", library_rst_ids) + testplan_path = self.__prepare_testplan( + testplan_path, + testplan, + testplan_rst_id + ) + return suites, testplan_path + + def __run_with_testplan(self, suites, testplan_path, options): + with self.monkeypatch.setenv("ALLURE_TESTPLAN_PATH", str(testplan_path)): + self.__run(suites, options) + + def __run(self, suites, options): + with fake_logger(AllureRobotRunner.LOGGER_PATH) as results: + options = self.__resolve_options(options) + robot.run(*suites, **options) + self.allure_results = results + + def __make_files_from_literals(self, extension, literals): + return [ + self.pytester.makefile( + extension, + **{name: content} + ) for name, content in (literals or {}).items() + ] + + def __copy_files(self, src_paths): + dst_paths = [] + for p in src_paths or []: + src = self.request.path / p + dst = self.pytester.path / Path(p).name + shutil.copyfile(src, dst) + dst_paths.append(dst) + return dst_paths + + def __make_all_files_from_rst(self, extension, rst_name_to_id): + return [ + self.__make_file_from_rst( + extension, + name, rst_id + ) for name, rst_id in (rst_name_to_id or {}).items() + ] + + def __make_file_from_rst(self, extension, name, rst_id): + return self.pytester.makefile( + extension, **{ + name: self.__get_from_rst(rst_id) + } + ) + + def __get_from_rst(self, rst_id): + return RstExampleTable.find_examples(self.request)[rst_id] + + def __prepare_testplan(self, content, path, rst_id): + if content: + path = self.pytester.makefile( + ".json", + json.dumps(content) + ) + elif rst_id: + path = self.pytester.makefile( + ".json", + self.__get_from_rst(rst_id) + ) + return path + + +@fixture +def robot_runner(request, pytester, monkeypatch): + return AllureRobotRunner(request, pytester, monkeypatch) \ No newline at end of file From dbbfc1546c9595cc7bfb8a5b8298e3eb6e06cf6d Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Sat, 18 Feb 2023 05:04:23 +0700 Subject: [PATCH 05/18] Allure-robotframework tests rewritten. Bunch of small fixes --- allure-behave/examples/testplan.rst | 2 +- allure-pytest/src/utils.py | 13 +- allure-python-commons-test/src/label.py | 5 +- allure-python-commons/src/mapping.py | 4 +- allure-robotframework/examples/attachment.rst | 14 +- .../examples/description.rst | 43 ++++ .../description/testcase_description.rst | 34 --- .../examples/fixture/testcase_fixture.rst | 46 ---- allure-robotframework/examples/label.rst | 91 +++++++ .../examples/label/__init__.rst | 5 - .../examples/label/labels_library.py | 11 - .../examples/label/testcase_bdd_label.rst | 10 - .../examples/label/testcase_custom_labels.rst | 9 - allure-robotframework/examples/link.rst | 100 ++++++++ .../examples/link/dynamic_link.rst | 13 - allure-robotframework/examples/link/link.rst | 54 ----- .../examples/status/status.rst | 21 -- .../examples/status/status_library.py | 6 - allure-robotframework/examples/step.rst | 37 +++ .../examples/step/builtin_step.rst | 6 - .../examples/step/outside_step.rst | 9 - .../examples/step/outside_step_library.py | 6 - allure-robotframework/examples/tag.rst | 108 +++++++++ allure-robotframework/examples/tag/tag.rst | 32 --- allure-robotframework/examples/testplan.rst | 101 ++++++++ .../examples/testplan/testplan.rst | 21 -- .../src/listener/allure_listener.py | 3 +- .../src/listener/allure_testplan.py | 8 +- allure-robotframework/src/listener/utils.py | 37 ++- .../test/attach/data_attach.robot | 22 -- .../test/attach/file_attach.robot | 11 - .../test/attach/foreign_library_attach.robot | 11 - .../description/testcase_description.robot | 28 --- .../test/fixture/suite_fixture.robot | 10 - .../test/fixture/testcase_fixture.robot | 57 ----- .../test/label/testcase_bdd_label.robot | 12 - .../test/label/testcase_custom_labels.robot | 12 - .../test/link/dynamic_link.robot | 22 -- allure-robotframework/test/link/link.robot | 37 --- .../test/link/suite_link.robot | 26 -- .../test/run_robot_library.py | 47 ---- .../test/status/status.robot | 37 --- .../test/step/builtin_step.robot | 17 -- .../test/step/outside_step.robot | 11 - allure-robotframework/test/tag/tag.robot | 27 --- .../test/test_allure_library.py | 133 ----------- .../test/testplan/testplan.robot | 16 -- .../allure_api/description/__init__.py | 0 .../description/description_test.py | 60 +++++ .../acceptance/allure_api/labels/__init__.py | 0 .../allure_api/labels/labels_test.py | 72 ++++++ .../acceptance/allure_api/links/__init__.py | 0 .../acceptance/allure_api/links/links_test.py | 141 +++++++++++ .../acceptance/allure_api/steps/__init__.py | 0 .../acceptance/allure_api/steps/steps_test.py | 27 +++ .../acceptance/allure_api/tags/__init__.py | 0 .../acceptance/allure_api/tags/tags_test.py | 111 +++++++++ .../allure_api/testplan/__init__.py | 0 .../allure_api/testplan/testplan_test.py | 23 ++ .../robotframework_support/__init__.py | 0 .../fixtures/__init__.py | 0 .../fixtures/fixture_test.py | 223 ++++++++++++++++++ .../statuses/__init__.py | 0 .../statuses/statuses_test.py | 100 ++++++++ tests/allure_robotframework/conftest.py | 4 +- tests/conftest.py | 6 +- 66 files changed, 1293 insertions(+), 859 deletions(-) create mode 100644 allure-robotframework/examples/description.rst delete mode 100644 allure-robotframework/examples/description/testcase_description.rst delete mode 100644 allure-robotframework/examples/fixture/testcase_fixture.rst create mode 100644 allure-robotframework/examples/label.rst delete mode 100644 allure-robotframework/examples/label/__init__.rst delete mode 100644 allure-robotframework/examples/label/labels_library.py delete mode 100644 allure-robotframework/examples/label/testcase_bdd_label.rst delete mode 100644 allure-robotframework/examples/label/testcase_custom_labels.rst create mode 100644 allure-robotframework/examples/link.rst delete mode 100644 allure-robotframework/examples/link/dynamic_link.rst delete mode 100644 allure-robotframework/examples/link/link.rst delete mode 100644 allure-robotframework/examples/status/status.rst delete mode 100644 allure-robotframework/examples/status/status_library.py create mode 100644 allure-robotframework/examples/step.rst delete mode 100644 allure-robotframework/examples/step/builtin_step.rst delete mode 100644 allure-robotframework/examples/step/outside_step.rst delete mode 100644 allure-robotframework/examples/step/outside_step_library.py create mode 100644 allure-robotframework/examples/tag.rst delete mode 100644 allure-robotframework/examples/tag/tag.rst create mode 100644 allure-robotframework/examples/testplan.rst delete mode 100644 allure-robotframework/examples/testplan/testplan.rst delete mode 100644 allure-robotframework/test/attach/data_attach.robot delete mode 100644 allure-robotframework/test/attach/file_attach.robot delete mode 100644 allure-robotframework/test/attach/foreign_library_attach.robot delete mode 100644 allure-robotframework/test/description/testcase_description.robot delete mode 100644 allure-robotframework/test/fixture/suite_fixture.robot delete mode 100644 allure-robotframework/test/fixture/testcase_fixture.robot delete mode 100644 allure-robotframework/test/label/testcase_bdd_label.robot delete mode 100644 allure-robotframework/test/label/testcase_custom_labels.robot delete mode 100644 allure-robotframework/test/link/dynamic_link.robot delete mode 100644 allure-robotframework/test/link/link.robot delete mode 100644 allure-robotframework/test/link/suite_link.robot delete mode 100644 allure-robotframework/test/run_robot_library.py delete mode 100644 allure-robotframework/test/status/status.robot delete mode 100644 allure-robotframework/test/step/builtin_step.robot delete mode 100644 allure-robotframework/test/step/outside_step.robot delete mode 100644 allure-robotframework/test/tag/tag.robot delete mode 100644 allure-robotframework/test/test_allure_library.py delete mode 100644 allure-robotframework/test/testplan/testplan.robot create mode 100644 tests/allure_robotframework/acceptance/allure_api/description/__init__.py create mode 100644 tests/allure_robotframework/acceptance/allure_api/description/description_test.py create mode 100644 tests/allure_robotframework/acceptance/allure_api/labels/__init__.py create mode 100644 tests/allure_robotframework/acceptance/allure_api/labels/labels_test.py create mode 100644 tests/allure_robotframework/acceptance/allure_api/links/__init__.py create mode 100644 tests/allure_robotframework/acceptance/allure_api/links/links_test.py create mode 100644 tests/allure_robotframework/acceptance/allure_api/steps/__init__.py create mode 100644 tests/allure_robotframework/acceptance/allure_api/steps/steps_test.py create mode 100644 tests/allure_robotframework/acceptance/allure_api/tags/__init__.py create mode 100644 tests/allure_robotframework/acceptance/allure_api/tags/tags_test.py create mode 100644 tests/allure_robotframework/acceptance/allure_api/testplan/__init__.py create mode 100644 tests/allure_robotframework/acceptance/allure_api/testplan/testplan_test.py create mode 100644 tests/allure_robotframework/acceptance/robotframework_support/__init__.py create mode 100644 tests/allure_robotframework/acceptance/robotframework_support/fixtures/__init__.py create mode 100644 tests/allure_robotframework/acceptance/robotframework_support/fixtures/fixture_test.py create mode 100644 tests/allure_robotframework/acceptance/robotframework_support/statuses/__init__.py create mode 100644 tests/allure_robotframework/acceptance/robotframework_support/statuses/statuses_test.py diff --git a/allure-behave/examples/testplan.rst b/allure-behave/examples/testplan.rst index 5b7a9391..5ade291c 100644 --- a/allure-behave/examples/testplan.rst +++ b/allure-behave/examples/testplan.rst @@ -122,7 +122,7 @@ Select test cases by allure id ------------------------------ If you link you scenarios to corresponding test cases with the :code:`as_id` -label, you can specify this ID instead to filter tests in the testplan: +label, you can filter them by those IDs instead: **testplan-1.feature**: diff --git a/allure-pytest/src/utils.py b/allure-pytest/src/utils.py index 146e37c6..cc59e244 100644 --- a/allure-pytest/src/utils.py +++ b/allure-pytest/src/utils.py @@ -110,9 +110,12 @@ def allure_package(item): def allure_name(item, parameters): - name = escape_name(item.name) + name = item.name title = allure_title(item) - return SafeFormatter().format(title, **{**parameters, **item.funcargs}) if title else name + return SafeFormatter().format( + title, + **{**parameters, **item.funcargs} + ) if title else name def allure_full_name(item: pytest.Item): @@ -120,7 +123,7 @@ def allure_full_name(item: pytest.Item): class_name = f".{item.parent.name}" if isinstance(item.parent, pytest.Class) else '' test = item.originalname if isinstance(item, pytest.Function) else item.name.split("[")[0] full_name = f'{package}{class_name}#{test}' - return escape_name(full_name) + return full_name def allure_suite_labels(item): @@ -139,10 +142,6 @@ def allure_suite_labels(item): return default_suite_labels -def escape_name(name): - return name.encode('ascii', 'backslashreplace').decode('unicode_escape') - - def get_outcome_status(outcome): _, exception, _ = outcome.excinfo or (None, None, None) return get_status(exception) diff --git a/allure-python-commons-test/src/label.py b/allure-python-commons-test/src/label.py index 9a3c9dc8..12782b06 100644 --- a/allure-python-commons-test/src/label.py +++ b/allure-python-commons-test/src/label.py @@ -1,8 +1,11 @@ from hamcrest import all_of +from hamcrest import anything from hamcrest import has_entry, has_item -def has_label(name, value): +def has_label(name, value=None): + if value is None: + value = anything() return has_entry( 'labels', has_item( diff --git a/allure-python-commons/src/mapping.py b/allure-python-commons/src/mapping.py index 6f61c651..737d3390 100644 --- a/allure-python-commons/src/mapping.py +++ b/allure-python-commons/src/mapping.py @@ -8,8 +8,8 @@ TAG_PREFIX = "allure" -semi_sep = re.compile(r"allure[\.\w]+:") -eq_sep = re.compile(r"allure[\.\w]+=") +semi_sep = re.compile(r"allure[\.\w]+[^:=]*:") +eq_sep = re.compile(r"allure[\.\w]+[^:=]*=") def allure_tag_sep(tag): diff --git a/allure-robotframework/examples/attachment.rst b/allure-robotframework/examples/attachment.rst index 10ace0eb..4a5f8043 100644 --- a/allure-robotframework/examples/attachment.rst +++ b/allure-robotframework/examples/attachment.rst @@ -73,16 +73,16 @@ It's possible to specify a name and a type of the attachment: ... name=my-file attachment_type=TEXT --------------------------------------------------- -Attachment of files created by 3rd party libraries --------------------------------------------------- +--------------------- +Automatic attachments +--------------------- -If you want to automatically attach files, created by some other library, you -can use the trick, described below. +If you want to automatically attach files, created by some library, you can use +the trick, described below. Lets say, we want to automatically attach screenshots, created by the `SeleniumLibrary's Capture Page Screenshot`_ keyword. To achieve that, lets -create a wrapper over our library of interest: +create a wrapper over the SeleniumLibrary: selenium_wrapper.py: ^^^^^^^^^^^^^^^^^^^^ @@ -118,7 +118,7 @@ The wrapper sets itself as the first library in the library resolution order of the Robot Framework. It uses the SeleniumLibrary under the hood, attaching the output file to the allure report. -Declare both the library and the wrapper in your .robot file: +Import both the library and the wrapper in your .robot file: .. code:: robotframework :name: selenium-suite diff --git a/allure-robotframework/examples/description.rst b/allure-robotframework/examples/description.rst new file mode 100644 index 00000000..6a7270e1 --- /dev/null +++ b/allure-robotframework/examples/description.rst @@ -0,0 +1,43 @@ +========================== +Description of a test case +========================== + +Allure-robotframework automatically converts documentation of a Robot Framework +test case into allure test case description. The documentation can be set with +the :code:`[Documentation]` test setting: + +.. code:: robotframework + :name: setting-singleline-robot + + *** Test Cases *** + Single line doc from the setting + [Documentation] This documentation will appear as allure description + No Operation + +The documentation may span multiple lines: + +.. code:: robotframework + :name: setting-multiline-robot + + *** Test Cases *** + Multiline doc from the setting + [Documentation] This documentation contains multiple lines of text. + ... It will also appear as allure description. + No Operation + +------------------------------------------ +The :code:`Set Test Documentation` keyword +------------------------------------------ + +The documentation can also be set dynamically with the +`Set Test Documentation keyword`_: + +.. code:: robotframework + :name: keyword-robot + + *** Test Cases *** + Multiline doc from the keyword + Set Test Documentation This documentation will appear as allure description. + + +.. _Set Test Documentation keyword: https://robotframework.org/robotframework/latest/libraries/BuiltIn.html#Set%20Test%20Documentation \ No newline at end of file diff --git a/allure-robotframework/examples/description/testcase_description.rst b/allure-robotframework/examples/description/testcase_description.rst deleted file mode 100644 index eb23b34e..00000000 --- a/allure-robotframework/examples/description/testcase_description.rst +++ /dev/null @@ -1,34 +0,0 @@ - -Allure supports text case description. All next examples are valid: - -.. code:: robotframework - - *** Test Cases *** - Single Line Description - [Documentation] Single line description - No Operation - - -.. code:: robotframework - - *** Test Cases *** - Multi Line Description - [Documentation] Multi line - ... description - No Operation - - -.. code:: robotframework - - *** Test Cases *** - Dynamic Description - [Documentation] Static description - Set Test Documentation Dynamic description - - -.. code:: robotframework - - *** Test Cases *** - Append Dynamic Description - [Documentation] Static description - Set Test Documentation Dynamic description append=yes \ No newline at end of file diff --git a/allure-robotframework/examples/fixture/testcase_fixture.rst b/allure-robotframework/examples/fixture/testcase_fixture.rst deleted file mode 100644 index c47e8f8f..00000000 --- a/allure-robotframework/examples/fixture/testcase_fixture.rst +++ /dev/null @@ -1,46 +0,0 @@ - -Allure report contains all next fixtures: - - - -.. code:: robotframework - - *** Keywords *** - Passed Setup - No Operation - - Failed Setup - Fail - - Passed Teardown - No Operation - - Failed Teardown - Fail - - *** Test Cases *** - Test Case With Test Setup - [Setup] Passed Setup - No Operation - - Test Case With Failed Test Setup - [Setup] Failed Setup - No Operation - - Test Case With Test Teardown - [Teardown] Passed Teardown - No Operation - - Test Case With Failed Test Teardown - [Teardown] Failed Teardown - No Operation - - Test Case With Test Setup And Teardown - [Setup] Passed Setup - [Teardown] Passed Teardown - No Operation - - Test Case With Test Failed Setup And Teardown - [Setup] Failed Setup - [Teardown] Failed Teardown - No Operation \ No newline at end of file diff --git a/allure-robotframework/examples/label.rst b/allure-robotframework/examples/label.rst new file mode 100644 index 00000000..ab55fa4e --- /dev/null +++ b/allure-robotframework/examples/label.rst @@ -0,0 +1,91 @@ +======================================= +Allure labels for Robot Framework tests +======================================= + +You can attach allure labels to test results using the following mechanisms: + +#. From a test case file with robot framework tags +#. From a test library, i.e., directly from python code + +----------------------------------------- +Add an allure label from a test case file +----------------------------------------- + +Apply a :code:`allure.label.:` tag to your test case. Such a tag +will be automatically converted to the allure label: + +.. code:: robotframework + :name: tag-custom-robot + + *** Test Cases *** + Test authored by John Doe + [Tags] allure.label.author:John Doe + No Operation + +You can define any label that way, being it a built-in or a custom one. If you +want to add a built-in label, a shorter version of the syntax can be used: + +.. code:: robotframework + :name: tag-builtin-robot + + *** Test Cases *** + Test with the pinned ID + [Tags] allure.id:1008 + ... allure.story:RF tags as allure labels + No Operation + +Use the :code:`Test Tags` setting to automatically apply tags to all test cases: + +.. code:: robotframework + :name: tag-setting-robot + + *** Settings *** + Test Tags allure.feature:Allure labels support + + *** Test Cases *** + Test with two BDD-labels + [Tags] allure.story:RF tags as allure labels + No Operation + +You can read more about applying tags to test cases here: `Tagging test cases`_. + +--------------------------------------- +Add an allure label from a test library +--------------------------------------- + +To add a label from python code use the :code:`@allure.label` decorator, +the :code:`allure.dynamic.label` function or their specialized alternatives for +built-in allure labels: + +my_library.py: +^^^^^^^^^^^^^^ +.. code:: python + :name: code-labels-library + + import allure + import os + + @allure.label("layer", "API") + @allure.severity(allure.severity_level.CRITICAL) + def open_api(host, port): + allure.dynamic.label("endpoint", f"{host}:{port}") + allure.dynamic.suite(f"Testing API at {host}") + +Now, if you import this library in a test case file and invoke its +:code:`[Open API]` keyword, the test case receives four allure lables: layer, +severity, endpoint and suite: + +.. code:: robotframework + :name: code-labels-robot + + *** Settings *** + Library ./my_library.py + + *** Test Cases *** + Test backend API + [Documentation] Four labels should be attached to this test result. + Open API host=localhost + ... port=443 + + +.. _`Tagging test cases`: https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#tagging-test-cases \ No newline at end of file diff --git a/allure-robotframework/examples/label/__init__.rst b/allure-robotframework/examples/label/__init__.rst deleted file mode 100644 index 334faa45..00000000 --- a/allure-robotframework/examples/label/__init__.rst +++ /dev/null @@ -1,5 +0,0 @@ - -.. code:: robotframework - - *** Settings *** - Force Tags allure.epic:Tag diff --git a/allure-robotframework/examples/label/labels_library.py b/allure-robotframework/examples/label/labels_library.py deleted file mode 100644 index 7ac7c297..00000000 --- a/allure-robotframework/examples/label/labels_library.py +++ /dev/null @@ -1,11 +0,0 @@ - -import allure - - -@allure.label('layer', 'UI') -def open_browser_with_ui_layer(): - pass - - -def add_custom_label(label_type, *labels): - allure.dynamic.label(label_type, *labels) diff --git a/allure-robotframework/examples/label/testcase_bdd_label.rst b/allure-robotframework/examples/label/testcase_bdd_label.rst deleted file mode 100644 index f08ac8fa..00000000 --- a/allure-robotframework/examples/label/testcase_bdd_label.rst +++ /dev/null @@ -1,10 +0,0 @@ - -.. code:: robotframework - - *** Settings *** - Force Tags allure.feature:Label - - *** Test Case *** - Test Cases With BDD Labels - [Tags] allure.story:Test case BDD labels - No Operation diff --git a/allure-robotframework/examples/label/testcase_custom_labels.rst b/allure-robotframework/examples/label/testcase_custom_labels.rst deleted file mode 100644 index 5ea84940..00000000 --- a/allure-robotframework/examples/label/testcase_custom_labels.rst +++ /dev/null @@ -1,9 +0,0 @@ - -.. code:: robotframework - - *** Settings *** - Library ./labels_library.py - *** Test Case *** - Test Case With Custom Labels - [Setup] Open Browser With UI Layer - Add Custom Label stand Alpha Beta diff --git a/allure-robotframework/examples/link.rst b/allure-robotframework/examples/link.rst new file mode 100644 index 00000000..a443bd1e --- /dev/null +++ b/allure-robotframework/examples/link.rst @@ -0,0 +1,100 @@ +====================================== +Allure links for Robot Framework tests +====================================== + +You can add a link to a test result in a way, similar to +`allure labels `_, either + +#. in a test case definition as a Robot Framework tag, or +#. in a test library (in python code) + +--------------------------------------- +A Robot Framework tag as an allure link +--------------------------------------- + +Use a :code:`allure.[.]:` tag to associate a link with +the test case. The :code:`` should be one of :code:`link`, +:code:`issue` or :code:`tms`. The :code:`.` part is optional and may be +omitted. In that case the value of the link is used as the text. + +The following example contains a test case with a link to the allure-python +repository: + +.. code:: robotframework + :name: tag-unnamed-robot + + *** Test Cases *** + Allure Plain Link + [Tags] allure.link:https://github.com/allure-framework/allure-python + No Operation + +In the next example the exact same link will be shown as 'allure-python' in the +report: + +.. code:: robotframework + :name: tag-named-robot + + *** Test Cases *** + Allure Plain Link + [Tags] allure.link.allure-python:https://github.com/allure-framework/allure-python + No Operation + +To link a test case to an issue, follow the next example: + +.. code:: robotframework + :name: tag-issue-robot + + *** Test Cases *** + Allure Issue Link + [Tags] allure.issue.ISSUE-1:https://github.com/allure-framework/allure-python/issues/1 + No Operation + +And you can also link a test case to the corresponding item in your test +management system: + +.. code:: robotframework + :name: tag-tms-robot + + *** Test Cases *** + Allure TMS Link + [Tags] allure.tms.TESTCASE-1:https://my-tms/test-cases/1 + No Operation + +Read more about applying tags to Robot Framework test cases in this article: +`Tagging test cases`_. + +-------------------------------------- +Add an allure link from a test library +-------------------------------------- + +Use allure.dynamic.link, allure.dynamic.issue or allure.dynamic.testcase to add +a link of an appropriate type. + +The following example shows the usage of all those functions at once: + +**my_lib.py**: + +.. code:: python + :name: code-links-lib + + import allure + + def add_three_links(): + allure.dynamic.link("https://github.com/allure-framework/allure-python", name="allure-python") + allure.dynamic.issue("https://github.com/allure-framework/allure-python/issues/1", name="ISSUE-1") + allure.dynamic.testcase("https://my-tms/test-cases/1", name="TESTCASE-1") + +**The test data**: + +.. code:: robotframework + :name: code-links-robot + + *** Settings *** + Library ./my_lib.py + + *** Test Cases *** + Allure Link Decorators and Functions + Add Three Links + + +.. _`Tagging test cases`: https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#tagging-test-cases \ No newline at end of file diff --git a/allure-robotframework/examples/link/dynamic_link.rst b/allure-robotframework/examples/link/dynamic_link.rst deleted file mode 100644 index c12ae647..00000000 --- a/allure-robotframework/examples/link/dynamic_link.rst +++ /dev/null @@ -1,13 +0,0 @@ -Dynamic Links ------ - -.. code:: robotframework - - *** Test Cases *** - Test Case With Dynamic Link - Evaluate allure.dynamic.issue('https://jira.com/browse/ISSUE-1', 'ISSUE-1') - ... modules=allure - Evaluate allure.dynamic.testcase('https://testrail.com/browse/TEST-1', 'TEST-1') - ... modules=allure - Evaluate allure.dynamic.link('https://homepage.com/', name='homepage') - ... modules=allure diff --git a/allure-robotframework/examples/link/link.rst b/allure-robotframework/examples/link/link.rst deleted file mode 100644 index df4567ca..00000000 --- a/allure-robotframework/examples/link/link.rst +++ /dev/null @@ -1,54 +0,0 @@ -Links ------ - -Issues are supported via tags prefixed with :code:`issue:` : - -.. code:: robotframework - - *** Test Cases *** - Test Case With Issue Link Without URL - [Tags] issue:ISSUE-1 - No Operation - - -.. code:: robotframework - - *** Test Cases *** - Test Case With Issue Link With URL - [Tags] issue:https://jira.com/browse/ISSUE-1 - No Operation - -TMS links are supported via tags prefixed with :code:`test_case:` : - -.. code:: robotframework - - *** Test Cases *** - Test Case With TMS Link Without URL - [Tags] test_case:TEST-1 - No Operation - - -.. code:: robotframework - - *** Test Cases *** - Test Case With TMS Link With URL - [Tags] test_case:https://testrail.com/browse/TEST-1 - No Operation - -Ordinary links are supported via tags prefixed with :code:`link:` : - -.. code:: robotframework - - *** Test Cases *** - Test Case With Unlabeled Link - [Tags] link:https://homepage.com/ - No Operation - -Link label can be specified by text placed in :code:`[square brackets]` : - -.. code:: robotframework - - *** Test Cases *** - Test Case With Labeled Link - [Tags] link:[Home Page]https://homepage.com/ - No Operation diff --git a/allure-robotframework/examples/status/status.rst b/allure-robotframework/examples/status/status.rst deleted file mode 100644 index 416446dc..00000000 --- a/allure-robotframework/examples/status/status.rst +++ /dev/null @@ -1,21 +0,0 @@ - -.. code:: robotframework - - *** Settings *** - Library ./status_library.py - - *** Test Cases *** - Failed Test Case With Message - Fail msg=Failed Details - - Failed Test Case With Traceback - Set Log Level DEBUG - Fail msg=Failed Details - - Failed Test Case With Python Traceback - Set Log Level DEBUG - Fail With Traceback Fail message - - Failed Test Case With Not Executed Step - Fail - Log This step should be skipped diff --git a/allure-robotframework/examples/status/status_library.py b/allure-robotframework/examples/status/status_library.py deleted file mode 100644 index b14e7ef3..00000000 --- a/allure-robotframework/examples/status/status_library.py +++ /dev/null @@ -1,6 +0,0 @@ - -from robot.libraries.BuiltIn import BuiltIn - - -def fail_with_traceback(traceback_message): - BuiltIn().fail(traceback_message) diff --git a/allure-robotframework/examples/step.rst b/allure-robotframework/examples/step.rst new file mode 100644 index 00000000..ef3afcba --- /dev/null +++ b/allure-robotframework/examples/step.rst @@ -0,0 +1,37 @@ +================== +Steps and substeps +================== + +All keywords of a Robot Framework test case are automatically converted to +allure steps. + +To add nested steps apply the :code:`@allure.step` decorator to a step function +or use the :code:`allure.step` function in your test library: + +**my_lib.py**: + +.. code:: python + :name: steps-lib + + import allure + + @allure.step("Substep {parameter}") + def substep_with_decorator(parameter): + pass + + def substep(): + with allure.step("Library substep"): + substep_with_decorator("A") + pass + +**The test data**: + +.. code:: robotframework + :name: steps-robot + + *** Settings *** + Library ./my_lib.py + + *** Test Cases *** + Allure substeps + Substep \ No newline at end of file diff --git a/allure-robotframework/examples/step/builtin_step.rst b/allure-robotframework/examples/step/builtin_step.rst deleted file mode 100644 index 9bc2ecde..00000000 --- a/allure-robotframework/examples/step/builtin_step.rst +++ /dev/null @@ -1,6 +0,0 @@ -.. code:: robotframework - - *** Test Cases *** - Log Builtin Keyword - Log The rose is red - Log the violet's blue \ No newline at end of file diff --git a/allure-robotframework/examples/step/outside_step.rst b/allure-robotframework/examples/step/outside_step.rst deleted file mode 100644 index 0b05ac93..00000000 --- a/allure-robotframework/examples/step/outside_step.rst +++ /dev/null @@ -1,9 +0,0 @@ - -.. code:: robotframework - - *** Settings *** - Library ./outside_step_library.py - - *** Test Cases *** - Use Library Keyword With Allure Step - Keyword With Allure Step \ No newline at end of file diff --git a/allure-robotframework/examples/step/outside_step_library.py b/allure-robotframework/examples/step/outside_step_library.py deleted file mode 100644 index d7b34597..00000000 --- a/allure-robotframework/examples/step/outside_step_library.py +++ /dev/null @@ -1,6 +0,0 @@ -import allure - - -def keyword_with_allure_step(): - with allure.step("Passed Step Inside Keyword"): - pass diff --git a/allure-robotframework/examples/tag.rst b/allure-robotframework/examples/tag.rst new file mode 100644 index 00000000..caeb28bf --- /dev/null +++ b/allure-robotframework/examples/tag.rst @@ -0,0 +1,108 @@ +===================================== +Allure tags for Robot Framework tests +===================================== + +You can apply an allure tag to your Robot Framework tests either in Robot +Framework test data (.robot files) or in a test library (.py files). + +---------------------------------------- +Allure tags in Robot Framework test data +---------------------------------------- + +All Robot Framework tags (except those, started with ``allure.``) are +automatically converted into allure tags: + +.. code:: robotframework + :name: tags-static-robot + + *** Test Cases *** + Distributed Legacy Test + [Tags] distributed legacy + No Operation + +The conversion happens when a test case is completed, so any changes to the set +of tags are reflected: + +.. code:: robotframework + :name: tags-dynamic-robot + + *** Test Cases *** + Isolated Test + [Tags] distributed + Set Tags isolated + Remove Tags distributed + +Be careful, because any failed step stops subsequent tag modifications: + +.. code:: robotframework + :name: tags-partial-robot + + *** Test Cases *** + Supposed to Be an Isolated Test + [Documentation] But ends up being a distributed one. + [Tags] distributed + Fail Unexpected failure + Set Tags isolated + Remove Tags distributed + +You can apply an allure tag explicitly, with the +:code:`allure.label.tag:` Robot Framework tag or its shorter version, +:code:`allure.tag:`: + +.. code:: robotframework + :name: tags-explicit-robot + + *** Test Cases *** + Explicit External Test + [Tags] allure.label.tag:explicit allure.tag:extrenal + No Operation + +All other Robot Framework tags, starting with :code:`allure.` are not converted: + +.. code:: robotframework + :name: tags-noconv-robot + + *** Test Cases *** + No Allure Tags + [Tags] allure.label.as_id:1008 allure.feature:Allure tags support + No Operation + +Read more about applying tags to Robot Framework test cases in this article: +`Tagging test cases`_. + +------------------------------------------- +Allure tags in Robot Framework test library +------------------------------------------- + +Use the :code:`@allure.tag` decorator or the :code:`allure.dynamic.tag` function +to add an allure tag to the current test result. + +**my_lib.py***: + +.. code:: python + :name: tags-code-lib + + import allure + + @allure.tag("external") + def connect_to_external_api(): + pass + + @allure.tag("legacy") + def download_service_list(): + connect_to_external_api() + allure.dynamic.tag("stateful") + +**Robot Framework test data**: + +.. code:: robotframework + :name: tags-code-robot + + *** Settings *** + Library ./my_lib.py + + *** Test Cases *** + Stateful External Legacy Test + Download Service List + +.. _`Tagging test cases`: https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#tagging-test-cases \ No newline at end of file diff --git a/allure-robotframework/examples/tag/tag.rst b/allure-robotframework/examples/tag/tag.rst deleted file mode 100644 index 1f0a1afb..00000000 --- a/allure-robotframework/examples/tag/tag.rst +++ /dev/null @@ -1,32 +0,0 @@ -Tags ----- - -Allure supports robotframework tags: - -.. code:: robotframework - - *** Test Cases *** - Test Case With Tags - [Tags] alpha bravo - No Operation - - -уou can manipulate with tags: - -.. code:: robotframework - - *** Test Cases *** - Test Case With Dynamic Tags - [Tags] alpha - Set Tags bravo - - -.. code:: robotframework - - *** Test Cases *** - Test Case With Removed Tags - [Tags] alpha bravo - Set Tags charlie - Remove Tags bravo - -allure will track it and show actual tags set. diff --git a/allure-robotframework/examples/testplan.rst b/allure-robotframework/examples/testplan.rst new file mode 100644 index 00000000..372cfe30 --- /dev/null +++ b/allure-robotframework/examples/testplan.rst @@ -0,0 +1,101 @@ +========================================= +Allure testplan for Robot Framework tests +========================================= + +You can filter Robot Framework test cases by an ID or by a name with an allure +testplan. + +#. Create a testplan file. +#. Set the :code:`ALLURE_TESTPLAN_PATH` environment variable to the testplan + path. +#. Run the Robot Framework with the :code:`allure_robotframework.testplan` + pre-run modifier. + +------------------- +Creating a testplan +------------------- + +Lets say, we have the following Robot Framework test data in the +:code:`testplan.robot` file: + +.. code:: robotframework + :name: testdata + + *** Test Cases *** + Selected by Name + No Operation + + Selected by ID + [Tags] allure.id:1008 + No Operation + + Not Selected + [Tags] allure.id:1009 + No Operation + +And we want to select only two test cases: + +#. The test case :code:`Selected by Name`. +#. The test case with ID :code:`1008`. + +To achieve that, we need the following testplan (lets say, we put it in the +:code:`testplan.json` file): + +.. code:: json + :name: testplan + + { + "version":"1.0", + "tests": [ + { + "selector": "Testplan.Selected by Name" + }, + {"id": "1008"} + ] + } + +------------------------------------------------------------- +Setting the :code:`ALLURE_TESTPLAN_PATH` environment variable +------------------------------------------------------------- + +Refer to the docs on the environment you are working in on how to set up an +environment variable. + +If you are working in Linux or MacOS and use bash or a similar shell, the most +convinient way is to put :code:`ALLURE_TESTPLAN_PATH=./testplan.json` before the +:code:`robot` command, or use the +:code:`export ALLURE_TESTPLAN_PATH=./testplan.json` statement. + +If you are using PowerShell, set up the variable with the +:code:`$Env:ALLURE_TESTPLAN_PATH = "./testplan.json"` statement. + +--------------------------- +Running the Robot Framework +--------------------------- + +The following example shows how to execute the abovementioned test cases with +the testplan enabled (this is for Linux, bash; if you are using another +OS/Shell, the invocation may looks differently): + +.. code:: shell + + ALLURE_TESTPLAN_PATH=./testplan.json robot --log NONE --report NONE --output NONE --extension robot --loglevel DEBUG --listener allure_robotframework:./allure-results --prerunmodifier allure_robotframework.testplan ./testplan.robot + +The test cases, listed in the plan, will be executed:: + + ============================================================================== + Testplan + ============================================================================== + Selected by Name | PASS | + ------------------------------------------------------------------------------ + Selected by ID | PASS | + ------------------------------------------------------------------------------ + Testplan | PASS | + 2 tests, 2 passed, 0 failed + ============================================================================== + Output: None + +You can read more about the CLI arguments here: +`the Robot Framework command line options`_. + +.. _`the Robot Framework command line options`: https://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#command-line-options \ No newline at end of file diff --git a/allure-robotframework/examples/testplan/testplan.rst b/allure-robotframework/examples/testplan/testplan.rst deleted file mode 100644 index 404d171d..00000000 --- a/allure-robotframework/examples/testplan/testplan.rst +++ /dev/null @@ -1,21 +0,0 @@ - -.. code:: robotframework - - *** Test Cases *** - First testcase - No Operation - - Second testcase - No Operation - - -.. code:: robotframework - - *** Test Cases *** - Test case with allure id - [Tags] allure.id=123 - No Operation - - One more case with allure id - [Tags] allure.id=777 - No Operation diff --git a/allure-robotframework/src/listener/allure_listener.py b/allure-robotframework/src/listener/allure_listener.py index a0ee14d3..7c8bccfa 100644 --- a/allure-robotframework/src/listener/allure_listener.py +++ b/allure-robotframework/src/listener/allure_listener.py @@ -17,7 +17,7 @@ from allure_robotframework.utils import get_allure_status from allure_robotframework.utils import get_allure_suites from allure_robotframework.utils import get_allure_parameters -from allure_robotframework.utils import allure_labels, allure_links, allure_tags +from allure_robotframework.utils import allure_labels, allure_links from allure_robotframework.types import RobotStatus, RobotLogLevel @@ -150,7 +150,6 @@ def stop_test(self, _, attributes, messages): test_result.labels.append(Label(name=LabelType.LANGUAGE, value=self._platform)) test_result.labels.append(Label(name=LabelType.HOST, value=self._host)) test_result.labels.append(Label(name=LabelType.THREAD, value=pool_id())) - test_result.labels.extend(allure_tags(attributes)) tags = attributes.get('tags', ()) test_result.labels.extend(allure_labels(tags)) test_result.statusDetails = StatusDetails(message=attributes.get('message'), diff --git a/allure-robotframework/src/listener/allure_testplan.py b/allure-robotframework/src/listener/allure_testplan.py index 865a5950..70e399fa 100644 --- a/allure-robotframework/src/listener/allure_testplan.py +++ b/allure-robotframework/src/listener/allure_testplan.py @@ -8,8 +8,12 @@ class allure_testplan(SuiteVisitor): def __init__(self): self.testplan = get_testplan() - self.allure_ids = [test["id"] for test in self.testplan] if self.testplan else [] - self.selectors = [test["selector"] for test in self.testplan] if self.testplan else [] + self.allure_ids = self.__to_set_by_item_key(self.testplan, "id") + self.selectors = self.__to_set_by_item_key(self.testplan, "selector") + + @staticmethod + def __to_set_by_item_key(items, key): + return {item[key] for item in items if key in item} def start_suite(self, suite): if self.testplan: diff --git a/allure-robotframework/src/listener/utils.py b/allure-robotframework/src/listener/utils.py index 18e6512a..a260ca6f 100644 --- a/allure-robotframework/src/listener/utils.py +++ b/allure-robotframework/src/listener/utils.py @@ -44,36 +44,47 @@ def get_allure_suites(longname): return labels -def allure_tags(attributes): - return [Label(LabelType.TAG, tag) for tag in attributes.get('tags', ()) if not allure_tag_sep(tag)] +def get_items_of_type_from_tags(tags, type): + return [ + item for item in map( + parse_tag, + tags + ) if isinstance(item, type) + ] + def allure_labels(tags): - parsed = [parse_tag(item) for item in tags] - return labels_set(list(filter(lambda x: isinstance(x, Label), parsed))) + return labels_set( + get_items_of_type_from_tags(tags, Label) + ) def allure_links(attributes, prefix): tags = attributes.get('tags', ()) - def is_link(link): - return link.startswith(f"{prefix}:") - - return [ + general_syntax_links = [ + link for link in get_items_of_type_from_tags( + tags, + Link + ) if link.type == prefix + ] + rf_specific_syntax_links = [ _create_link_from_tag( prefix, tag - ) for tag in tags if is_link(tag) + ) for tag in tags if tag.startswith(f"{prefix}:") ] + return general_syntax_links + rf_specific_syntax_links + def _parse_link(link): lnk_val = link.split(':', 1)[1] or 'unknown' - lnk_label = search(r'\[.+\]', lnk_val) + lnk_label = search(r'^\[(.+)\]', lnk_val) if lnk_label: - lnk_label = lnk_label.group(0) - lnk_val = lnk_val.strip(lnk_label) - lnk_label = lnk_label.strip('[]') + lnk_val = lnk_val[lnk_label.end():] + lnk_label = lnk_label.group(1) else: lnk_label = lnk_val diff --git a/allure-robotframework/test/attach/data_attach.robot b/allure-robotframework/test/attach/data_attach.robot deleted file mode 100644 index e7d8a266..00000000 --- a/allure-robotframework/test/attach/data_attach.robot +++ /dev/null @@ -1,22 +0,0 @@ -*** Settings *** -Library ../run_robot_library.py -Library ../test_allure_library.py -Suite Setup Run exampe - - -*** Keywords *** -Run exampe - ${allure_report} Run Robot With Allure examples/attach/data_attach.rst - Set Suite Variable ${report} ${allure_report} - - -*** Test Case *** -Data Attachment - ${test_case} Should Has Test Case ${report} Data Attachment - ${step} Should Has Step ${test_case} AllureLibrary.Attach - Should Has Attachment ${step} - -Data Attachment With Name And Type - ${test_case} Should Has Test Case ${report} Data Attachment With Name And Type - ${step} Should Has Step ${test_case} AllureLibrary.Attach - Should Has Attachment ${step} name=links \ No newline at end of file diff --git a/allure-robotframework/test/attach/file_attach.robot b/allure-robotframework/test/attach/file_attach.robot deleted file mode 100644 index 5f2c1563..00000000 --- a/allure-robotframework/test/attach/file_attach.robot +++ /dev/null @@ -1,11 +0,0 @@ -*** Settings *** -Library ../run_robot_library.py -Library ../test_allure_library.py - - -*** Test Case *** -Data Attachment - ${allure_report} Run Robot With Allure examples/attach/file_attach.rst - ${test_case} Should Has Test Case ${allure_report} File Attachment - ${step} Should Has Step ${test_case} AllureLibrary.Attach File - Should Has Attachment ${step} \ No newline at end of file diff --git a/allure-robotframework/test/attach/foreign_library_attach.robot b/allure-robotframework/test/attach/foreign_library_attach.robot deleted file mode 100644 index e77931f6..00000000 --- a/allure-robotframework/test/attach/foreign_library_attach.robot +++ /dev/null @@ -1,11 +0,0 @@ -*** Settings *** -Library ../run_robot_library.py -Library ../test_allure_library.py - - -*** Test Case *** -Override Library Keyword And Make Allure Attachment - ${allure_report} Run Robot With Allure examples/attach/foreign_library_attach.rst - ${test_case} Should Has Test Case ${allure_report} Override Library Keyword And Make Allure Attachment - ${step} Should Has Step ${test_case} foreign_library_helper.Capture Page Screenshot - Should Has Attachment ${step} name=screenshot \ No newline at end of file diff --git a/allure-robotframework/test/description/testcase_description.robot b/allure-robotframework/test/description/testcase_description.robot deleted file mode 100644 index 18567a85..00000000 --- a/allure-robotframework/test/description/testcase_description.robot +++ /dev/null @@ -1,28 +0,0 @@ -*** Settings *** -Library ../run_robot_library.py -Library ../test_allure_library.py -Suite Setup Run exampe - - -*** Keywords *** -Run exampe - ${allure_report} Run Robot With Allure examples/description/testcase_description.rst - Set Suite Variable ${report} ${allure_report} - - -*** Test Cases *** -Single Line Description - ${test_case} Should Has Test Case ${report} Single Line Description - Should Has Description ${test_case} Single line description - -Multi Line Description - ${test_case} Should Has Test Case ${report} Multi Line Description - Should Has Description ${test_case} Multi line\ndescription - -Dynamic Description - ${test_case} Should Has Test Case ${report} Dynamic Description - Should Has Description ${test_case} Dynamic description - -Append Dynamic Description - ${test_case} Should Has Test Case ${report} Append Dynamic Description - Should Has Description ${test_case} Static description Dynamic description \ No newline at end of file diff --git a/allure-robotframework/test/fixture/suite_fixture.robot b/allure-robotframework/test/fixture/suite_fixture.robot deleted file mode 100644 index bc1a95ac..00000000 --- a/allure-robotframework/test/fixture/suite_fixture.robot +++ /dev/null @@ -1,10 +0,0 @@ -#*** Settings *** -#Suite Teardown Suite Setup Keyword -# -#*** Test Cases *** -#Demo Test Case With Suite Setup -# No Operation -# -#*** Keywords *** -#Suite Setup Keyword -# Fail msg=Failed Details \ No newline at end of file diff --git a/allure-robotframework/test/fixture/testcase_fixture.robot b/allure-robotframework/test/fixture/testcase_fixture.robot deleted file mode 100644 index 6e73f2bb..00000000 --- a/allure-robotframework/test/fixture/testcase_fixture.robot +++ /dev/null @@ -1,57 +0,0 @@ -#*** Settings *** -#Library ../run_robot_library.py -#Library ../test_allure_library.py -#Suite Setup Run exampe -# -# -#*** Keywords *** -#Run exampe -# ${allure_report} Run Robot With Allure examples/fixture/sf2/ -# Set Suite Variable ${report} ${allure_report} -# -# -#*** Test Cases *** -#Test Case With Test Setup -# ${test_case} Should Has Test Case ${report} Test Case With Test Setup -# ${fixture} Should Has Before Fixture ${test_case} Passed Setup -# Should Has Step ${fixture} BuiltIn.No Operation -# -#Test Case With Failed Test Setup -# ${test_case} Should Has Test Case ${report} Test Case With Failed Test Setup -# Should Has Status ${test_case} failed -# ${fixture} Should Has Before Fixture ${test_case} Failed Setup -# Should Has Status ${fixture} failed -# ${step} Should Has Step ${fixture} BuiltIn.Fail -# Should Has Status ${step} failed -# -#Test Case With Test Teardown -# ${test_case} Should Has Test Case ${report} Test Case With Test Teardown -# ${fixture} Should Has After Fixture ${test_case} Passed Teardown -# Should Has Step ${fixture} BuiltIn.No Operation -# -#Test Case With Failed Test Teardown -# ${test_case} Should Has Test Case ${report} Test Case With Failed Test Teardown -# Should Has Status ${test_case} failed -# ${fixture} Should Has After Fixture ${test_case} Failed Teardown -# Should Has Status ${fixture} failed -# ${step} Should Has Step ${fixture} BuiltIn.Fail -# Should Has Status ${step} failed -# -#Test Case With Test Setup And Teardown -# ${test_case} Should Has Test Case ${report} Test Case With Test Setup And Teardown -# ${fixture} Should Has Before Fixture ${test_case} Passed Setup -# Should Has Step ${fixture} BuiltIn.No Operation -# ${fixture} Should Has After Fixture ${test_case} Passed Teardown -# Should Has Step ${fixture} BuiltIn.No Operation -# -#Test Case With Test Failed Setup And Teardown -# ${test_case} Should Has Test Case ${report} Test Case With Test Failed Setup And Teardown -# Should Has Status ${test_case} failed -# ${fixture} Should Has Before Fixture ${test_case} Failed Setup -# Should Has Status ${fixture} failed -# ${step} Should Has Step ${fixture} BuiltIn.Fail -# Should Has Status ${step} failed -# ${fixture} Should Has After Fixture ${test_case} Failed Teardown -# Should Has Status ${fixture} failed -# ${step} Should Has Step ${fixture} BuiltIn.Fail -# Should Has Status ${step} failed diff --git a/allure-robotframework/test/label/testcase_bdd_label.robot b/allure-robotframework/test/label/testcase_bdd_label.robot deleted file mode 100644 index 5cb1c90a..00000000 --- a/allure-robotframework/test/label/testcase_bdd_label.robot +++ /dev/null @@ -1,12 +0,0 @@ -*** Settings *** -Library ../run_robot_library.py -Library ../test_allure_library.py - - -*** Test Case *** -Run Without options - ${allure_report} Run Robot With Allure examples/label - ${test_case} Should Has Test Case ${allure_report} Test Cases With BDD Labels - Should Has Label ${test_case} epic Tag - Should Has Label ${test_case} feature Label - Should Has Label ${test_case} story Test case BDD labels diff --git a/allure-robotframework/test/label/testcase_custom_labels.robot b/allure-robotframework/test/label/testcase_custom_labels.robot deleted file mode 100644 index 1a0d9bb1..00000000 --- a/allure-robotframework/test/label/testcase_custom_labels.robot +++ /dev/null @@ -1,12 +0,0 @@ -*** Settings *** -Library ../run_robot_library.py -Library ../test_allure_library.py - - -*** Test Case *** -Run Without options - ${allure_report} Run Robot With Allure examples/label/testcase_custom_labels.rst - ${test_case} Should Has Test Case ${allure_report} Test Case With Custom Labels - Should Has Label ${test_case} layer UI - Should Has Label ${test_case} stand Alpha - Should Has Label ${test_case} stand Beta diff --git a/allure-robotframework/test/link/dynamic_link.robot b/allure-robotframework/test/link/dynamic_link.robot deleted file mode 100644 index 90ac2c41..00000000 --- a/allure-robotframework/test/link/dynamic_link.robot +++ /dev/null @@ -1,22 +0,0 @@ -*** Settings *** -Library ../run_robot_library.py -Library ../test_allure_library.py -Suite Setup Run test case with dynamic link - - -*** Keywords *** -Run test case with dynamic link - ${allure_report} Run Robot With Allure examples/link/dynamic_link.rst - ${test_case} Should Has Test Case ${allure_report} Test Case With Dynamic Link - Set Suite Variable ${test_case} - - -*** Test Case *** -Test Case With Dynamic Links - ${test case} Should Has issue To https://jira.com/browse/ISSUE-1 With Name ISSUE-1 - ${test case} Should Has test_case To https://testrail.com/browse/TEST-1 With Name TEST-1 - ${test case} Should Has link To https://homepage.com/ With Name homepage - -*** Keywords *** -${test case} Should Has ${link type} To ${url} With Name ${name} - Should Has Link ${test_case} ${url} ${link type} ${name} diff --git a/allure-robotframework/test/link/link.robot b/allure-robotframework/test/link/link.robot deleted file mode 100644 index 76ffb0bf..00000000 --- a/allure-robotframework/test/link/link.robot +++ /dev/null @@ -1,37 +0,0 @@ -*** Settings *** -Library ../run_robot_library.py -Library ../test_allure_library.py -Suite Setup Run example - - -*** Keywords *** -Run example - ${allure_report} Run Robot With Allure examples/link/link.rst - Set Suite Variable ${report} ${allure_report} - - -*** Test Case *** -Test Case With Issue Link Without URL - ${test_case} Should Has Test Case ${report} Test Case With Issue Link Without URL - Should Has Link ${test_case} ISSUE-1 issue - -Test Case With Issue Link With URL - ${test_case} Should Has Test Case ${report} Test Case With Issue Link With URL - Should Has Link ${test_case} https://jira.com/browse/ISSUE-1 issue - -Test Case With TMS Link Without URL - ${test_case} Should Has Test Case ${report} Test Case With TMS Link Without URL - Should Has Link ${test_case} TEST-1 test_case - -Test Case With TMS Link With URL - ${test_case} Should Has Test Case ${report} Test Case With TMS Link With URL - Should Has Link ${test_case} https://testrail.com/browse/TEST-1 test_case - -Test Case With Unlabeled Link - ${test_case} Should Has Test Case ${report} Test Case With Unlabeled Link - Should Has Link ${test_case} https://homepage.com/ link - -Test Case With Labeled Link - ${test_case} Should Has Test Case ${report} Test Case With Labeled Link - Should Has Link ${test_case} https://homepage.com/ link Home Page - diff --git a/allure-robotframework/test/link/suite_link.robot b/allure-robotframework/test/link/suite_link.robot deleted file mode 100644 index 19030a90..00000000 --- a/allure-robotframework/test/link/suite_link.robot +++ /dev/null @@ -1,26 +0,0 @@ -*** Settings *** -Library ../run_robot_library.py -Library ../test_allure_library.py - - -*** Variables *** -${TC_LOG} | *Settings* | | |\n -... | Metadata | Link | https://github.com/allure-framework |\n -... | | | |\n -... | *Test Cases* | | |\n -... | Demo Test Case In Suite With link | | |\n -... | | No Operation | |\n - - -*** Test Cases *** -Test Case In Suite With link - No Operation -# ToDo: deprecated, need to revove from code -# ${tmp_dir}= Make Temp Dir -# ${test_case_dir}= Make Dir ${tmp_dir} test -# ${test_case}= Make Test Case ${test_case_dir} link.robot ${TC_LOG} -# ${allure_report}= Robot Run With Allure ${tmp_dir} ${test_case} -# ${tc_matcher}= Should Has Test Case ${allure_report} Demo Test Case In Suite With link -# Should has link ${allure_report} ${tc_matcher} https://github.com/allure-framework - -# ToDo: other link types \ No newline at end of file diff --git a/allure-robotframework/test/run_robot_library.py b/allure-robotframework/test/run_robot_library.py deleted file mode 100644 index b23c3c2a..00000000 --- a/allure-robotframework/test/run_robot_library.py +++ /dev/null @@ -1,47 +0,0 @@ -import os -from tempfile import mkdtemp, mkstemp -from robot import run -from multiprocessing import Process -from allure_commons_test.report import AllureReport - - -def run_robot_with_allure(*args, **kwargs): - root = os.path.abspath(os.path.join(__file__, "..", "..")) - targets = map(lambda target: os.path.join(root, target), args) - tmp_path = mkdtemp(dir=os.environ.get('TEST_TMP', '/tmp')) - - if "testplan" in kwargs: - # kwargs.pop("testplan") - kwargs["prerunmodifier"] = "allure_robotframework.testplan" - file, filename = mkstemp(suffix=".json", dir=tmp_path) - os.environ["ALLURE_TESTPLAN_PATH"] = filename - with os.fdopen(file, 'w') as tmp: - tmp.write(kwargs["testplan"]) - - def run_robot(path, **kw): - - # ToDo: fix it (_core not works correctly with multiprocessing) - # import allure_commons - # import importlib - # importlib.reload(allure_commons._core) - # - # - - from allure_robotframework import allure_robotframework - - listener = allure_robotframework(logger_path=tmp_path) - stdout_file = os.path.abspath(os.path.join(tmp_path, "..", "stdout.txt")) - output_path = os.path.abspath(os.path.join(tmp_path, "..")) - - with open(stdout_file, 'w+') as stdout: - options = {"listener": listener, "outputdir": output_path, "stdout": stdout, "extension": "rst"} - options.update(kw) - run(path, **options) - - robot_process = Process(target=run_robot, args=targets, kwargs=kwargs) - robot_process.start() - robot_process.join() - - os.environ.pop("ALLURE_TESTPLAN_PATH", None) - - return AllureReport(tmp_path) diff --git a/allure-robotframework/test/status/status.robot b/allure-robotframework/test/status/status.robot deleted file mode 100644 index 273a8d9d..00000000 --- a/allure-robotframework/test/status/status.robot +++ /dev/null @@ -1,37 +0,0 @@ -*** Settings *** -Library ../run_robot_library.py -Library ../test_allure_library.py - - -*** Test Case *** -Failed Test Case With Message - ${allure_report} Run Robot With Allure examples/status/status.rst - ${test_case} Should Has Test Case ${allure_report} Failed Test Case With Message - Should Has Status ${test_case} failed - Should Has Status Detail With Message ${test_case} Failed Details - ${step} Should Has Step ${test_case} BuiltIn.Fail - Should Has Status ${step} failed - Should Has Status Detail With Message ${step} Failed Details - -Failed Test Case With Traceback - ${allure_report} Run Robot With Allure examples/status/status.rst - ${test_case} Should Has Test Case ${allure_report} Failed Test Case With Traceback - Should Has Status ${test_case} failed - Should Has Status Detail With Traceback ${test_case} Traceback (most recent call last):\n${SPACE * 2}None - ${step} Should Has Step ${test_case} BuiltIn.Fail - Should Has Status ${step} failed - -Failed Test Case With Python Traceback - ${allure_report} Run Robot With Allure examples/status/status.rst - ${test_case} Should Has Test Case ${allure_report} Failed Test Case With Python Traceback - Should Has Status ${test_case} failed - Should Has Status Detail With Traceback ${test_case} fail_with_traceback\n${SPACE * 4}BuiltIn().fail(traceback_message) - ${step} Should Has Step ${test_case} status_library.Fail With Traceback - Should Has Status ${step} failed - -Failed Test Case With Not Executed Step - ${allure_report} Run Robot With Allure examples/status/status.rst - ${test_case} Should Has Test Case ${allure_report} Failed Test Case With Not Executed Step - Should Has Status ${test_case} failed - ${step} Should Has Step ${test_case} BuiltIn.Log - Should Has Status ${step} skipped diff --git a/allure-robotframework/test/step/builtin_step.robot b/allure-robotframework/test/step/builtin_step.robot deleted file mode 100644 index 8d63945e..00000000 --- a/allure-robotframework/test/step/builtin_step.robot +++ /dev/null @@ -1,17 +0,0 @@ -*** Settings *** -Library ../run_robot_library.py -Library ../test_allure_library.py - - -*** Test Case *** -Use Library Keyword With Allure Step - ${allure_report} Run Robot With Allure examples/step/builtin_step.rst - ${test_case} Should Has Test Case ${allure_report} Log Builtin Keyword - ${step} Should Has Step ${test_case} BuiltIn.Log - Should Has Parameter ${step} arg1 The rose is red - ${step} Should Has Step ${test_case} BuiltIn.Log - Should Has Parameter ${step} arg1 the violet's blue - -# ToDo: loglevel -# ToDo: Log Many -# ToDo: Comment \ No newline at end of file diff --git a/allure-robotframework/test/step/outside_step.robot b/allure-robotframework/test/step/outside_step.robot deleted file mode 100644 index e7b6206a..00000000 --- a/allure-robotframework/test/step/outside_step.robot +++ /dev/null @@ -1,11 +0,0 @@ -*** Settings *** -Library ../run_robot_library.py -Library ../test_allure_library.py - - -*** Test Case *** -Use Library Keyword With Allure Step - ${allure_report} Run Robot With Allure examples/step/outside_step.rst - ${test_case} Should Has Test Case ${allure_report} Use Library Keyword With Allure Step - ${step} Should Has Step ${test_case} outside_step_library.Keyword With Allure Step - Should Has Step ${step} Passed Step Inside Keyword \ No newline at end of file diff --git a/allure-robotframework/test/tag/tag.robot b/allure-robotframework/test/tag/tag.robot deleted file mode 100644 index b1f10d64..00000000 --- a/allure-robotframework/test/tag/tag.robot +++ /dev/null @@ -1,27 +0,0 @@ -*** Settings *** -Library ../run_robot_library.py -Library ../test_allure_library.py -Suite Setup Run exampe - - -*** Keywords *** -Run exampe - ${allure_report} Run Robot With Allure examples/tag/tag.rst - Set Suite Variable ${report} ${allure_report} - - -*** Test Case *** -Test Case With Tags - ${test_case} Should Has Test Case ${report} Test Case With Tags - Should Has Tag ${test_case} alpha - Should Has Tag ${test_case} bravo - -Test Case With Dynamic Tags - ${test_case} Should Has Test Case ${report} Test Case With Dynamic Tags - Should Has Tag ${test_case} alpha - Should Has Tag ${test_case} bravo - -Test Case With Removed Tags - ${test_case} Should Has Test Case ${report} Test Case With Removed Tags - Should Has Tag ${test_case} alpha - Should Has Tag ${test_case} charlie \ No newline at end of file diff --git a/allure-robotframework/test/test_allure_library.py b/allure-robotframework/test/test_allure_library.py deleted file mode 100644 index 0dd97c27..00000000 --- a/allure-robotframework/test/test_allure_library.py +++ /dev/null @@ -1,133 +0,0 @@ -from functools import partial -from hamcrest import assert_that -from hamcrest import not_, anything -from allure_commons_test.report import has_test_case -from allure_commons_test.result import has_description -from allure_commons_test.result import has_step -from allure_commons_test.result import with_status -from allure_commons_test.result import has_parameter -from allure_commons_test.result import has_link -from allure_commons_test.result import has_status_details -from allure_commons_test.result import with_message_contains -from allure_commons_test.result import with_trace_contains -from allure_commons_test.result import has_attachment -from allure_commons_test.label import has_severity -from allure_commons_test.label import has_tag -from allure_commons_test.label import has_label -from allure_commons_test.container import has_container -from allure_commons_test.container import has_before, has_after - - -def match(matcher, *args): - for i, arg in enumerate(args): - if not hasattr(arg, '__call__'): - matcher = partial(matcher, arg) - else: - matcher = partial(matcher, match(arg, *args[i+1:])) - break - return matcher() - - -def should_has_test_case(allure_report, test_case): - test_case_matcher = partial(match, has_test_case, test_case) - assert_that(allure_report, test_case_matcher()) - return allure_report, test_case_matcher - - -def should_has_tag(context, tag): - allure_report, test_case_matcher = context - tag_matcher = partial(test_case_matcher, has_tag, tag) - assert_that(allure_report, tag_matcher()) - - -def should_not_has_tag(conetxt, tag): - allure_report, test_case_matcher = conetxt - tag_matcher = partial(test_case_matcher, not_, has_tag, tag) - assert_that(allure_report, tag_matcher()) - - -def should_has_severity(context, severity): - allure_report, test_case_matcher = context - severity_matcher = partial(test_case_matcher, has_severity, severity) - assert_that(allure_report, severity_matcher()) - - -def should_not_has_severity(context): - allure_report, test_case_matcher = context - severity_matcher = partial(test_case_matcher, not_, has_severity, anything) - assert_that(allure_report, severity_matcher()) - - -def should_has_step(context, step): - allure_report, item_matcher = context - step_matcher = partial(item_matcher, has_step, step) - assert_that(allure_report, step_matcher()) - return allure_report, step_matcher - - -def should_not_has_step(context, step): - allure_report, item_matcher = context - step_matcher = partial(item_matcher, not_, has_step, step) - assert_that(allure_report, step_matcher()) - - -def should_has_attachment(context, attach_type=None, name=None): - allure_report, item_matcher = context - matcher = partial(item_matcher, has_attachment, attach_type, name) - assert_that(allure_report, matcher()) - - -def should_has_description(context, description): - allure_report, test_case_matcher = context - description_matcher = partial(test_case_matcher, has_description, description) - assert_that(allure_report, description_matcher()) - - -def should_has_before_fixture(context, fixture): - allure_report, test_case_matcher = context - fixture_matcher = partial(test_case_matcher, has_container, allure_report, has_before, fixture) - assert_that(allure_report, fixture_matcher()) - return allure_report, fixture_matcher - - -def should_has_after_fixture(context, fixture): - allure_report, test_case_matcher = context - fixture_matcher = partial(test_case_matcher, has_container, allure_report, has_after, fixture) - assert_that(allure_report, fixture_matcher()) - return allure_report, fixture_matcher - - -def should_has_status(context, status): - allure_report, item_matcher = context - status_matcher = partial(item_matcher, with_status, status) - assert_that(allure_report, status_matcher()) - - -def should_has_parameter(context, name, value): - allure_report, item_matcher = context - matcher = partial(item_matcher, has_parameter, name, value) - assert_that(allure_report, matcher()) - - -def should_has_status_detail_with_message(conext, message): - allure_report, item_matcher = conext - matcher = partial(item_matcher, has_status_details, with_message_contains, message) - assert_that(allure_report, matcher()) - - -def should_has_status_detail_with_traceback(conext, traceback): - allure_report, item_matcher = conext - matcher = partial(item_matcher, has_status_details, with_trace_contains, traceback) - assert_that(allure_report, matcher()) - - -def should_has_link(context, link, link_type=None, link_name=None): - allure_report, test_case_matcher = context - matcher = partial(test_case_matcher, has_link, link, link_type, link_name) - assert_that(allure_report, matcher()) - - -def should_has_label(context, name, value): - allure_report, test_case_matcher = context - matcher = partial(test_case_matcher, has_label, name, value) - assert_that(allure_report, matcher()) diff --git a/allure-robotframework/test/testplan/testplan.robot b/allure-robotframework/test/testplan/testplan.robot deleted file mode 100644 index 7ba18820..00000000 --- a/allure-robotframework/test/testplan/testplan.robot +++ /dev/null @@ -1,16 +0,0 @@ -*** Settings *** -Library ../run_robot_library.py -Library ../test_allure_library.py - - -*** Variables ** -${PLAN_A} \{ -... "version":"1.0", -... "tests": [ -... { "id": "123", "selector": "Second testcase"} -... ] -... \} - -*** Test Case *** -Failed Test Case With Message - ${allure_report} Run Robot With Allure examples/testplan/testplan.rst testplan=${PLAN_A} \ No newline at end of file diff --git a/tests/allure_robotframework/acceptance/allure_api/description/__init__.py b/tests/allure_robotframework/acceptance/allure_api/description/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_robotframework/acceptance/allure_api/description/description_test.py b/tests/allure_robotframework/acceptance/allure_api/description/description_test.py new file mode 100644 index 00000000..18a43d24 --- /dev/null +++ b/tests/allure_robotframework/acceptance/allure_api/description/description_test.py @@ -0,0 +1,60 @@ +""" ./allure-robotframework/examples/description.rst """ + +from hamcrest import assert_that +from tests.allure_robotframework.conftest import AllureRobotRunner +from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_description + +def test_single_line_description_from_setting( + robot_runner: AllureRobotRunner +): + robot_runner.run_robotframework( + suite_rst_ids={"description.robot": "setting-singleline-robot"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Description.Single line doc from the setting", + has_description( + "This documentation will appear as allure description" + ) + ) + ) + + +def test_miltiline_description_from_setting( + robot_runner: AllureRobotRunner +): + robot_runner.run_robotframework( + suite_rst_ids={"description.robot": "setting-multiline-robot"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Description.Multiline doc from the setting", + has_description( + "This documentation contains multiple lines of text.\n" + "It will also appear as allure description." + ) + ) + ) + + +def test_miltiline_description_from_keyword( + robot_runner: AllureRobotRunner +): + robot_runner.run_robotframework( + suite_rst_ids={"description.robot": "keyword-robot"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Description.Multiline doc from the keyword", + has_description( + "This documentation will appear as allure description." + ) + ) + ) \ No newline at end of file diff --git a/tests/allure_robotframework/acceptance/allure_api/labels/__init__.py b/tests/allure_robotframework/acceptance/allure_api/labels/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_robotframework/acceptance/allure_api/labels/labels_test.py b/tests/allure_robotframework/acceptance/allure_api/labels/labels_test.py new file mode 100644 index 00000000..0f7ed8a0 --- /dev/null +++ b/tests/allure_robotframework/acceptance/allure_api/labels/labels_test.py @@ -0,0 +1,72 @@ +""" ./allure-robotframework/examples/label.rst """ + +from allure import severity_level +from hamcrest import assert_that +from tests.allure_robotframework.conftest import AllureRobotRunner +from allure_commons_test.report import has_test_case +from allure_commons_test.label import has_label +from allure_commons_test.label import has_feature +from allure_commons_test.label import has_story +from allure_commons_test.label import has_severity +from allure_commons_test.label import has_suite + +def test_custom_label_from_robot_tag(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"labels.robot": "tag-custom-robot"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Labels.Test authored by John Doe", + has_label("author", "John Doe") + ) + ) + + +def test_builtin_label_from_robot_tag(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"labels.robot": "tag-builtin-robot"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Labels.Test with the pinned ID", + has_label("as_id", "1008"), + has_story("RF tags as allure labels") + ) + ) + + +def test_robot_tag_from_settings(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"labels.robot": "tag-setting-robot"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Labels.Test with two BDD-labels", + has_feature("Allure labels support"), + has_story("RF tags as allure labels") + ) + ) + + +def test_labels_from_test_library(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"labels.robot": "code-labels-robot"}, + library_rst_ids={"my_library.py": "code-labels-library"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Labels.Test backend API", + has_label("layer", "API"), + has_severity(severity_level.CRITICAL), + has_label("endpoint", "localhost:443"), + has_suite("Testing API at localhost") + ) + ) \ No newline at end of file diff --git a/tests/allure_robotframework/acceptance/allure_api/links/__init__.py b/tests/allure_robotframework/acceptance/allure_api/links/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_robotframework/acceptance/allure_api/links/links_test.py b/tests/allure_robotframework/acceptance/allure_api/links/links_test.py new file mode 100644 index 00000000..8196c0b4 --- /dev/null +++ b/tests/allure_robotframework/acceptance/allure_api/links/links_test.py @@ -0,0 +1,141 @@ +""" ./allure-robotframework/examples/link.rst """ + +from hamcrest import assert_that +from tests.allure_robotframework.conftest import AllureRobotRunner +from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_link +from allure_commons_test.result import has_issue_link +from allure_commons_test.result import has_test_case_link + +def test_link_from_robot_tag(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"links.robot": "tag-unnamed-robot"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Links.Allure Plain Link", + has_link( + "https://github.com/allure-framework/allure-python", + "link", + "https://github.com/allure-framework/allure-python" + ) + ) + ) + + +def test_named_link_from_robot_tag(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"links.robot": "tag-named-robot"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Links.Allure Plain Link", + has_link( + "https://github.com/allure-framework/allure-python", + "link", + "allure-python" + ) + ) + ) + + +def test_issue_from_robot_tag(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"links.robot": "tag-issue-robot"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Links.Allure Issue Link", + has_issue_link( + "https://github.com/allure-framework/allure-python/issues/1", + "ISSUE-1" + ) + ) + ) + + +def test_tms_from_robot_tag(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"links.robot": "tag-tms-robot"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Links.Allure TMS Link", + has_test_case_link( + "https://my-tms/test-cases/1", + "TESTCASE-1" + ) + ) + ) + + +def test_links_from_code(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"links.robot": "code-links-robot"}, + library_rst_ids={"my_lib.py": "code-links-lib"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Links.Allure Link Decorators and Functions", + has_link( + "https://github.com/allure-framework/allure-python", + "link", + "allure-python" + ), + has_issue_link( + "https://github.com/allure-framework/allure-python/issues/1", + "ISSUE-1" + ), + has_test_case_link( + "https://my-tms/test-cases/1", + "TESTCASE-1" + ) + ) + ) + + +def test_rf_specific_tag_syntax(docstring, robot_runner: AllureRobotRunner): + """ + *** Test Cases *** + RF-Specific Tag Syntax + [Tags] link:[allure-python]https://github.com/allure-framework/allure-python + ... link:https://github.com/allure-framework/allure-python/issues + ... issue:ISSUE-1 + ... issue:https://github.com/allure-framework/allure-python/issues/1 + ... issue:[ISSUE-1]https://github.com/allure-framework/allure-python/issues/1 + ... tms:TESTCASE-1 + ... tms:https://my-tms/test-cases/1 + ... tms:[TESTCASE-1]https://my-tms/test-cases/1 + No Operation + """ + + robot_runner.run_robotframework( + suite_literals={"links.robot": docstring} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Links.RF-Specific Tag Syntax", + has_link( + "https://github.com/allure-framework/allure-python", + "link", + "allure-python" + ), + has_link( + "https://github.com/allure-framework/allure-python/issues", + "link", + "https://github.com/allure-framework/allure-python/issues" + ) + ) + ) \ No newline at end of file diff --git a/tests/allure_robotframework/acceptance/allure_api/steps/__init__.py b/tests/allure_robotframework/acceptance/allure_api/steps/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_robotframework/acceptance/allure_api/steps/steps_test.py b/tests/allure_robotframework/acceptance/allure_api/steps/steps_test.py new file mode 100644 index 00000000..179f7f9f --- /dev/null +++ b/tests/allure_robotframework/acceptance/allure_api/steps/steps_test.py @@ -0,0 +1,27 @@ +""" ./allure-robotframework/examples/step.rst """ + +from hamcrest import assert_that +from tests.allure_robotframework.conftest import AllureRobotRunner +from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_step + + +def test_library_steps(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"steps.robot": "steps-robot"}, + library_rst_ids={"my_lib.py": "steps-lib"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Steps.Allure substeps", + has_step( + "my_lib.Substep", + has_step( + "Library substep", + has_step("Substep 'A'") + ) + ) + ) + ) \ No newline at end of file diff --git a/tests/allure_robotframework/acceptance/allure_api/tags/__init__.py b/tests/allure_robotframework/acceptance/allure_api/tags/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_robotframework/acceptance/allure_api/tags/tags_test.py b/tests/allure_robotframework/acceptance/allure_api/tags/tags_test.py new file mode 100644 index 00000000..7fb4d6b0 --- /dev/null +++ b/tests/allure_robotframework/acceptance/allure_api/tags/tags_test.py @@ -0,0 +1,111 @@ +""" ./allure-robotframework/examples/tag.rst """ + +from hamcrest import assert_that, all_of, not_ +from tests.allure_robotframework.conftest import AllureRobotRunner +from allure_commons_test.report import has_test_case +from allure_commons_test.label import has_tag +from allure_commons_test.label import has_label + +def test_robot_tags(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"tags.robot": "tags-static-robot"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Tags.Distributed Legacy Test", + has_tag("distributed"), + has_tag("legacy") + ) + ) + + +def test_modified_robot_tags(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"tags.robot": "tags-dynamic-robot"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Tags.Isolated Test", + all_of( + has_tag("isolated"), + not_( + has_tag("distributed") + ) + ) + ) + ) + + +def test_robot_tags_not_modified_because_of_failure(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"tags.robot": "tags-partial-robot"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Tags.Supposed to Be an Isolated Test", + all_of( + has_tag("distributed"), + not_( + has_tag("isolated") + ) + ) + ) + ) + + +def test_explicit_tags(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"tags.robot": "tags-explicit-robot"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Tags.Explicit External Test", + all_of( + has_tag("explicit"), + has_tag("extrenal") + ) + ) + ) + + +def test_rf_tags_starting_with_allure_not_added(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"tags.robot": "tags-noconv-robot"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Tags.No Allure Tags", + not_( + has_label("tag") + ) + ) + ) + + +def test_tags_from_library(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"tags.robot": "tags-code-robot"}, + library_rst_ids={"my_lib.py": "tags-code-lib"} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Tags.Stateful External Legacy Test", + all_of( + has_tag("stateful"), + has_tag("external"), + has_tag("legacy") + ) + ) + ) \ No newline at end of file diff --git a/tests/allure_robotframework/acceptance/allure_api/testplan/__init__.py b/tests/allure_robotframework/acceptance/allure_api/testplan/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_robotframework/acceptance/allure_api/testplan/testplan_test.py b/tests/allure_robotframework/acceptance/allure_api/testplan/testplan_test.py new file mode 100644 index 00000000..49c44d51 --- /dev/null +++ b/tests/allure_robotframework/acceptance/allure_api/testplan/testplan_test.py @@ -0,0 +1,23 @@ +""" ./allure-robotframework/examples/testplan.rst """ + +from allure_commons_test.report import has_test_case +from hamcrest import assert_that, all_of, not_ +from tests.allure_robotframework.conftest import AllureRobotRunner + +def test_testplan(robot_runner: AllureRobotRunner): + robot_runner.run_robotframework( + suite_rst_ids={"testplan.robot": "testdata"}, + testplan_rst_id="testplan", + options={"prerunmodifier": "allure_robotframework.testplan"} + ) + + assert_that( + robot_runner.allure_results, + all_of( + has_test_case("Testplan.Selected by Name"), + has_test_case("Selected by ID"), + not_( + has_test_case("Testplan.Not Selected") + ) + ) + ) \ No newline at end of file diff --git a/tests/allure_robotframework/acceptance/robotframework_support/__init__.py b/tests/allure_robotframework/acceptance/robotframework_support/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_robotframework/acceptance/robotframework_support/fixtures/__init__.py b/tests/allure_robotframework/acceptance/robotframework_support/fixtures/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_robotframework/acceptance/robotframework_support/fixtures/fixture_test.py b/tests/allure_robotframework/acceptance/robotframework_support/fixtures/fixture_test.py new file mode 100644 index 00000000..29180cba --- /dev/null +++ b/tests/allure_robotframework/acceptance/robotframework_support/fixtures/fixture_test.py @@ -0,0 +1,223 @@ +from hamcrest import assert_that +from tests.allure_robotframework.conftest import AllureRobotRunner +from allure_commons_test.report import has_test_case +from allure_commons_test.container import has_container +from allure_commons_test.container import has_before +from allure_commons_test.container import has_after +from allure_commons_test.result import with_status +from allure_commons_test.result import has_status_details +from allure_commons_test.result import with_message_contains + +def test_setup(docstring, robot_runner: AllureRobotRunner): + """ + *** Keywords *** + Fixture + No Operation + + *** Test Cases *** + Test Under Test + [Setup] Fixture + No Operation + """ + + robot_runner.run_robotframework( + suite_literals={"fixture.robot": docstring} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Fixture.Test Under Test", + has_container( + robot_runner.allure_results, + has_before( + "Fixture", + with_status("passed") + ) + ) + ) + ) + + +def test_failed_setup(docstring, robot_runner: AllureRobotRunner): + """ + *** Keywords *** + Fixture + Fail Reason + + *** Test Cases *** + Test Under Test + [Setup] Fixture + No Operation + """ + + robot_runner.run_robotframework( + suite_literals={"fixture.robot": docstring} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Fixture.Test Under Test", + has_container( + robot_runner.allure_results, + has_before( + "Fixture", + with_status("failed"), + has_status_details( + with_message_contains("Reason") + ) + ) + ) + ) + ) + + +def test_teardown(docstring, robot_runner: AllureRobotRunner): + """ + *** Keywords *** + Fixture + No Operation + + *** Test Cases *** + Test Under Test + [Teardown] Fixture + No Operation + """ + + robot_runner.run_robotframework( + suite_literals={"fixture.robot": docstring} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Fixture.Test Under Test", + has_container( + robot_runner.allure_results, + has_after( + "Fixture", + with_status("passed") + ) + ) + ) + ) + + +def test_failed_teardown(docstring, robot_runner: AllureRobotRunner): + """ + *** Keywords *** + Fixture + Fail Reason + + *** Test Cases *** + Test Under Test + [Teardown] Fixture + No Operation + """ + + robot_runner.run_robotframework( + suite_literals={"fixture.robot": docstring} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Fixture.Test Under Test", + has_container( + robot_runner.allure_results, + has_after( + "Fixture", + with_status("failed"), + has_status_details( + with_message_contains("Reason") + ) + ) + ) + ) + ) + + +def test_setup_teardown(docstring, robot_runner: AllureRobotRunner): + """ + *** Keywords *** + Setup Fixture + No Operation + + Teardown Fixture + No Operation + + *** Test Cases *** + Test Under Test + [Setup] Setup Fixture + [Teardown] Teardown Fixture + No Operation + """ + + robot_runner.run_robotframework( + suite_literals={"fixture.robot": docstring} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Fixture.Test Under Test", + has_container( + robot_runner.allure_results, + has_before( + "Setup Fixture", + with_status("passed") + ), + has_after( + "Teardown Fixture", + with_status("passed") + ) + ) + ) + ) + + +def test_failed_setup_teardown(docstring, robot_runner: AllureRobotRunner): + """ + *** Keywords *** + Setup Fixture + Fail Setup fail reason + + Teardown Fixture + Fail Teardown fail reason + + *** Test Cases *** + Test Under Test + [Setup] Setup Fixture + [Teardown] Teardown Fixture + No Operation + """ + + robot_runner.run_robotframework( + suite_literals={"fixture.robot": docstring} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Fixture.Test Under Test", + has_container( + robot_runner.allure_results, + has_before( + "Setup Fixture", + with_status("failed"), + has_status_details( + with_message_contains("Setup fail reason") + ) + ), + has_after( + "Teardown Fixture", + with_status("failed"), + has_status_details( + with_message_contains("Teardown fail reason") + ) + ) + ) + ) + ) + diff --git a/tests/allure_robotframework/acceptance/robotframework_support/statuses/__init__.py b/tests/allure_robotframework/acceptance/robotframework_support/statuses/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/allure_robotframework/acceptance/robotframework_support/statuses/statuses_test.py b/tests/allure_robotframework/acceptance/robotframework_support/statuses/statuses_test.py new file mode 100644 index 00000000..a66ad43b --- /dev/null +++ b/tests/allure_robotframework/acceptance/robotframework_support/statuses/statuses_test.py @@ -0,0 +1,100 @@ +from hamcrest import assert_that +from doctest import script_from_examples +from tests.allure_robotframework.conftest import AllureRobotRunner +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status +from allure_commons_test.result import has_status_details +from allure_commons_test.result import with_message_contains +from allure_commons_test.result import with_trace_contains +from allure_commons_test.result import has_step + +def test_failed_test(docstring, robot_runner: AllureRobotRunner): + """ + *** Test Cases *** + Failed Test Case + Fail msg=Reason + """ + + robot_runner.run_robotframework( + suite_literals={"status.robot": docstring} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Status.Failed Test Case", + with_status("failed"), + has_status_details( + with_message_contains("Reason"), + with_trace_contains("AssertionError: Reason") + ) + ) + ) + + +def test_failed_in_library(docstring, robot_runner: AllureRobotRunner): + """ + *** Settings *** + Library ./library.py + + *** Test Cases *** + Failed Test Case + Fail In Library + """ + + library = script_from_examples( + """ + >>> from robot.libraries.BuiltIn import BuiltIn + >>> def fail_in_library(): + ... BuiltIn().fail("Reason") + """ + ) + + robot_runner.run_robotframework( + suite_literals={"status.robot": docstring}, + library_literals={"library.py": library} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Status.Failed Test Case", + with_status("failed"), + has_status_details( + with_message_contains("Reason"), + with_trace_contains("library.py\", line 3, in fail_in_library") + ), + has_step( + "library.Fail In Library", + with_status("failed"), + has_status_details( + with_message_contains("Reason"), + with_trace_contains("library.py\", line 3, in fail_in_library") + ) + ) + ) + ) + + +def test_steps_after_failed_are_skipped(docstring, robot_runner: AllureRobotRunner): + """ + *** Test Cases *** + Failed Test Case + Fail + Log This step is skipped + """ + + robot_runner.run_robotframework( + suite_literals={"status.robot": docstring} + ) + + assert_that( + robot_runner.allure_results, + has_test_case( + "Status.Failed Test Case", + has_step( + "BuiltIn.Log", + with_status("skipped") + ) + ) + ) \ No newline at end of file diff --git a/tests/allure_robotframework/conftest.py b/tests/allure_robotframework/conftest.py index 001c1c83..bc968eea 100644 --- a/tests/allure_robotframework/conftest.py +++ b/tests/allure_robotframework/conftest.py @@ -129,8 +129,8 @@ def __prepare_example( return suites, testplan_path def __run_with_testplan(self, suites, testplan_path, options): - with self.monkeypatch.setenv("ALLURE_TESTPLAN_PATH", str(testplan_path)): - self.__run(suites, options) + self.monkeypatch.setenv("ALLURE_TESTPLAN_PATH", str(testplan_path)) + self.__run(suites, options) def __run(self, suites, options): with fake_logger(AllureRobotRunner.LOGGER_PATH) as results: diff --git a/tests/conftest.py b/tests/conftest.py index 4b058242..dc8b4109 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -174,9 +174,9 @@ def __create_and_cache(node, path): f"Document, referred by {node.nodeid}, " f"doesn't exist at {filepath}" ) - examples = RstExampleTable.load_examples(filepath) - node.stash[RstExampleTable.STASH_KEY] = examples - return examples + table = RstExampleTable(filepath) + node.stash[RstExampleTable.STASH_KEY] = table + return table @staticmethod def __filter(node): From 940de64f07130482c7f527112aea3590755f19a4 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Thu, 23 Feb 2023 23:39:13 +0700 Subject: [PATCH 06/18] Refactor tests into a separate directory The tests directory follows the following pattern: / Where a test type could be an acceptance test (i.e., end-to-end test on the allure integration itself), a unit test, a test on a defect, a test on an external library support, etc. Acceptance tests could be further devided into two groups: tests to ensure the user API implementation is correct, and tests that check if allure supports the framework itself properly. Also contains following changes: - all: refactor test execution logic into the abstract runner class - allure_pytest: fix typo in duration_time_test.py - allure_pytest: fix end enable skipped and xfail tests - allure_pytest, allure_nose2: remove unnecessary parametrization from some tests - allure_pytest: change --log-cli-level to more common --log-level to test log capturing - allure_pytest, allure_pytest_bdd: move act phase from fixtures to tests - allure_pytest: add less_than assertion to the duration tests - allure_pytest: the fix second assertion on test_one_fixture_on_two_tests - allure_pytest: add ids to some parametrized tests - allure_pytest: fix test file names for them to be collected by pytest - allure_pytest: remove duplicated parameter set from test_step_parameters - allure_pytest: remove unnecessary test on missing testplan - allure_python: remove unicode tests (obsolete since py2 support dropped) - all: disable pytest automatic plugin loading - allure_pytest: move testplan and pluginmanager tests to acceptance group --- allure-nose2/pyproject.toml | 5 +- allure-nose2/src/plugin.py | 2 +- allure-nose2/test/example_loader.py | 29 - allure-nose2/test/example_runner.py | 62 -- allure-nose2/test/labels/test_bdd_labels.py | 85 --- .../test/parametrized/test_parametrized.py | 59 -- allure-nose2/test/result/test_fullname.py | 39 -- allure-nose2/test/result/test_status.py | 71 -- .../examples/scenario-outline/outline.feature | 10 - .../scenario-outline/scenario_outline_test.py | 22 - .../examples/simple-scenario/scenario.feature | 5 - .../examples/simple-scenario/scenario_test.py | 21 - allure-pytest-bdd/setup.py | 1 - allure-python-commons-test/src/result.py | 11 +- allure-robotframework/pyproject.toml | 2 +- .../allure_api/attachment/attachment_test.py | 21 +- .../description/description_test.py | 31 +- .../allure_api/labels/label_test.py | 7 +- .../acceptance/allure_api/links/link_test.py | 29 +- .../allure_api/severities/severity_test.py | 14 +- .../acceptance/allure_api/tags/tag_test.py | 8 +- .../allure_api/testplan/testplan_test.py | 77 +-- .../background/background_test.py | 13 +- .../behave_cmd/behave_cmd_test.py | 15 +- .../behave_support/hooks/hook_test.py | 6 +- .../hooks/test-data/context-step-hooks.py | 1 - .../scenario_outline_test.py | 9 +- .../behave_support/scenarios/scenario_test.py | 17 +- .../behave_support/steps/behave_step_test.py | 30 +- tests/allure_behave/behave_runner.py | 195 ++++++ tests/allure_behave/conftest.py | 259 +------- tests/allure_behave/defects/issue717_test.py | 2 + .../test => tests/allure_nose2}/__init__.py | 0 .../allure_nose2/acceptance}/__init__.py | 0 .../acceptance/allure_api}/__init__.py | 0 .../acceptance/allure_api/labels}/__init__.py | 0 .../allure_api/labels/test_bdd_labels.py | 102 +++ .../acceptance/nose2_support}/__init__.py | 0 .../nose2_support/parametrized}/__init__.py | 0 .../parametrized/test_parametrized.py | 58 ++ .../nose2_support/result}/__init__.py | 0 .../nose2_support/result/test_fullname.py | 50 ++ .../nose2_support/result/test_status.py | 96 +++ .../nose2_support/with_mp}/__init__.py | 0 .../nose2_support/with_mp/test_mp.py | 6 +- tests/allure_nose2/conftest.py | 7 + tests/allure_nose2/nose2_runner.py | 39 ++ .../attachment/attachment_class_test.py | 17 +- .../attachment/attachment_fixture_test.py | 49 +- .../attachment/attachment_hook_test.py | 88 +-- .../attachment_parametrized_test.py | 38 +- .../attachment/attachment_step_test.py | 74 ++- .../acceptance/attachment/attachment_test.py | 53 +- .../acceptance/capture/capture_attach_test.py | 94 +-- .../description/description_test.py | 72 +- .../description/dynamic_description_test.py | 38 +- .../display_name/display_name_test.py | 132 ++-- .../display_name/dynamic_display_name_test.py | 18 +- .../acceptance/duration/duration_time_test.py | 97 ++- .../fixture/fixture_finalized_test.py | 50 +- .../acceptance/fixture/fixture_test.py | 548 +++++++++------- .../fixture/parametrized_fixture_test.py | 80 ++- .../acceptance/fixture/yield_fixture_test.py | 34 +- .../acceptance/history_id/history_id_test.py | 34 +- .../acceptance/label/bdd/bdd_label_test.py | 54 +- .../label/bdd/dynamic_bdd_label_test.py | 93 ++- .../acceptance/label/bdd/select_bdd_test.py | 81 ++- .../label/custom/custom_label_test.py | 20 +- .../label/custom/select_custom_label_test.py | 49 +- .../label/id/set_testcase_id_test.py | 36 +- .../acceptance/label/manual/manual_test.py | 34 +- .../label/package/regression_test.py | 29 +- .../label/severity/class_severity_test.py | 84 ++- .../label/severity/module_severity_test.py | 66 +- .../label/severity/select_severity_test.py | 17 +- .../label/severity/severity_test.py | 18 +- .../acceptance/label/suite/custom_suite.py | 15 - .../label/suite/custom_suite_test.py | 21 + .../label/suite/default_suite_test.py | 89 +-- .../suite/module_level_custom_suite_test.py | 18 +- .../acceptance/label/tag/tag_test.py | 138 ++-- .../acceptance/link/dynamic_link_test.py | 79 ++- .../acceptance/link/link_pattern_test.py | 10 +- .../acceptance/link/link_test.py | 66 +- .../parametrization/metafunc_test.py | 58 +- .../parametrization/parametrization_test.py | 80 ++- .../__init__.py | 0 .../pytest_get_allure_plugin_test.py | 26 + .../status/base_call_status_test.py | 111 ++-- .../status/base_setup_status_test.py | 202 +++--- .../status/base_step_status_test.py | 108 +-- .../status/base_teardown_status_test.py | 18 +- .../status/skip_call_status_test.py | 98 ++- .../status/skip_setup_status_test.py | 42 +- .../status/skip_step_status_test.py | 34 +- .../status/skip_teardown_status_test.py | 10 +- .../status/xfail_call_status_test.py | 131 ++-- .../status/xfail_setup_status_test.py | 84 ++- .../status/xfail_step_status_test.py | 37 +- .../status/xfail_teardown_status_test.py | 10 +- .../acceptance/step/outside_step_test.py | 91 ++- .../acceptance/step/step_parameters.py | 57 -- .../acceptance/step/step_parameters_test.py | 42 ++ .../acceptance/step/step_placeholder_test.py | 28 +- .../acceptance/step/step_test.py | 75 ++- ...st_step_with_several_step_inside_thread.py | 62 +- .../select_test_from_testplan_test.py | 79 +-- .../unicode_identifier_test.py | 37 -- tests/allure_pytest/conftest.py | 7 + .../pytest_check/pytest_check_test.py | 27 - .../pytest_doctest/pytest_doctest_test.py | 67 -- .../pytest_flakes/pytest_flakes_test.py | 30 - .../pytest_get_allure_plugin_test.py | 19 - .../pytest_rerunfailures_test.py | 31 - .../pytest_xdist/__init__.py | 0 .../pytest_xdist/pytest_xdist_select_test.py | 32 - .../__init__.py | 0 .../pytest_check/__init__.py | 0 .../pytest_check/pytest_check_test.py | 38 ++ .../pytest_doctest/__init__.py | 0 .../pytest_doctest/pytest_doctest_test.py | 63 ++ .../pytest_flakes/__init__.py | 0 .../pytest_flakes/pytest_flakes_test.py | 35 + .../pytest_lazy_fixture/__init__.py | 0 .../pytest_lazy_fixture_test.py | 53 +- .../pytest_rerunfailures}/__init__.py | 0 .../pytest_rerunfailures_test.py | 43 ++ .../pytest_xdist}/__init__.py | 0 .../pytest_xdist/pytest_xdist_select_test.py | 39 ++ tests/allure_pytest/pytest_runner.py | 206 ++++++ .../acceptance/scenario_outline_test.py | 49 +- .../acceptance/scenario_test.py | 43 +- tests/allure_pytest_bdd/conftest.py | 10 +- .../allure_api/attachment/attachment_test.py | 13 +- .../description/description_test.py | 5 +- .../allure_api/labels/labels_test.py | 5 +- .../acceptance/allure_api/links/links_test.py | 5 +- .../acceptance/allure_api/steps/steps_test.py | 4 +- .../acceptance/allure_api/tags/tags_test.py | 5 +- .../allure_api/testplan/testplan_test.py | 5 +- .../fixtures/fixture_test.py | 4 +- .../statuses/statuses_test.py | 5 +- tests/allure_robotframework/conftest.py | 193 +----- tests/allure_robotframework/robot_runner.py | 122 ++++ tests/conftest.py | 279 +------- tests/e2e.py | 615 ++++++++++++++++++ tests/requirements.txt | 3 - 147 files changed, 4449 insertions(+), 3175 deletions(-) delete mode 100644 allure-nose2/test/example_loader.py delete mode 100644 allure-nose2/test/example_runner.py delete mode 100644 allure-nose2/test/labels/test_bdd_labels.py delete mode 100644 allure-nose2/test/parametrized/test_parametrized.py delete mode 100644 allure-nose2/test/result/test_fullname.py delete mode 100644 allure-nose2/test/result/test_status.py delete mode 100644 allure-pytest-bdd/examples/scenario-outline/outline.feature delete mode 100644 allure-pytest-bdd/examples/scenario-outline/scenario_outline_test.py delete mode 100644 allure-pytest-bdd/examples/simple-scenario/scenario.feature delete mode 100644 allure-pytest-bdd/examples/simple-scenario/scenario_test.py create mode 100644 tests/allure_behave/behave_runner.py rename {allure-nose2/test => tests/allure_nose2}/__init__.py (100%) rename {allure-nose2/test/labels => tests/allure_nose2/acceptance}/__init__.py (100%) rename {allure-nose2/test/parametrized => tests/allure_nose2/acceptance/allure_api}/__init__.py (100%) rename {allure-nose2/test/result => tests/allure_nose2/acceptance/allure_api/labels}/__init__.py (100%) create mode 100644 tests/allure_nose2/acceptance/allure_api/labels/test_bdd_labels.py rename {allure-nose2/test/with_mp => tests/allure_nose2/acceptance/nose2_support}/__init__.py (100%) rename {allure-pytest-bdd/examples/scenario-outline => tests/allure_nose2/acceptance/nose2_support/parametrized}/__init__.py (100%) create mode 100644 tests/allure_nose2/acceptance/nose2_support/parametrized/test_parametrized.py rename {allure-pytest-bdd/examples/simple-scenario => tests/allure_nose2/acceptance/nose2_support/result}/__init__.py (100%) create mode 100644 tests/allure_nose2/acceptance/nose2_support/result/test_fullname.py create mode 100644 tests/allure_nose2/acceptance/nose2_support/result/test_status.py rename tests/{allure_pytest/acceptance/fixture/function_scope => allure_nose2/acceptance/nose2_support/with_mp}/__init__.py (100%) rename allure-nose2/test/with_mp/test_pm.py => tests/allure_nose2/acceptance/nose2_support/with_mp/test_mp.py (58%) create mode 100644 tests/allure_nose2/conftest.py create mode 100644 tests/allure_nose2/nose2_runner.py delete mode 100644 tests/allure_pytest/acceptance/label/suite/custom_suite.py create mode 100644 tests/allure_pytest/acceptance/label/suite/custom_suite_test.py rename tests/allure_pytest/acceptance/{unicode_identifier => pytest_pluginmanager}/__init__.py (100%) create mode 100644 tests/allure_pytest/acceptance/pytest_pluginmanager/pytest_get_allure_plugin_test.py delete mode 100644 tests/allure_pytest/acceptance/step/step_parameters.py create mode 100644 tests/allure_pytest/acceptance/step/step_parameters_test.py delete mode 100644 tests/allure_pytest/acceptance/unicode_identifier/unicode_identifier_test.py create mode 100644 tests/allure_pytest/conftest.py delete mode 100644 tests/allure_pytest/external_packages_support/pytest_check/pytest_check_test.py delete mode 100644 tests/allure_pytest/external_packages_support/pytest_doctest/pytest_doctest_test.py delete mode 100644 tests/allure_pytest/external_packages_support/pytest_flakes/pytest_flakes_test.py delete mode 100644 tests/allure_pytest/external_packages_support/pytest_pluginmanager/pytest_get_allure_plugin_test.py delete mode 100644 tests/allure_pytest/external_packages_support/pytest_rerunfailures/pytest_rerunfailures_test.py delete mode 100644 tests/allure_pytest/external_packages_support/pytest_xdist/__init__.py delete mode 100644 tests/allure_pytest/external_packages_support/pytest_xdist/pytest_xdist_select_test.py rename tests/allure_pytest/{external_packages_support => externals}/__init__.py (100%) rename tests/allure_pytest/{external_packages_support => externals}/pytest_check/__init__.py (100%) create mode 100644 tests/allure_pytest/externals/pytest_check/pytest_check_test.py rename tests/allure_pytest/{external_packages_support => externals}/pytest_doctest/__init__.py (100%) create mode 100644 tests/allure_pytest/externals/pytest_doctest/pytest_doctest_test.py rename tests/allure_pytest/{external_packages_support => externals}/pytest_flakes/__init__.py (100%) create mode 100644 tests/allure_pytest/externals/pytest_flakes/pytest_flakes_test.py rename tests/allure_pytest/{external_packages_support => externals}/pytest_lazy_fixture/__init__.py (100%) rename tests/allure_pytest/{external_packages_support => externals}/pytest_lazy_fixture/pytest_lazy_fixture_test.py (50%) rename tests/allure_pytest/{external_packages_support/pytest_pluginmanager => externals/pytest_rerunfailures}/__init__.py (100%) create mode 100644 tests/allure_pytest/externals/pytest_rerunfailures/pytest_rerunfailures_test.py rename tests/allure_pytest/{external_packages_support/pytest_rerunfailures => externals/pytest_xdist}/__init__.py (100%) create mode 100644 tests/allure_pytest/externals/pytest_xdist/pytest_xdist_select_test.py create mode 100644 tests/allure_pytest/pytest_runner.py create mode 100644 tests/allure_robotframework/robot_runner.py create mode 100644 tests/e2e.py delete mode 100644 tests/requirements.txt diff --git a/allure-nose2/pyproject.toml b/allure-nose2/pyproject.toml index 0916246f..fc8c91d9 100644 --- a/allure-nose2/pyproject.toml +++ b/allure-nose2/pyproject.toml @@ -1,4 +1,3 @@ [tool.poe.tasks] -linter = "flake8 ./src ./test" -tests = """nose2 --plugin=test.example_loader --plugin=allure_nose2.plugin --allure - --current-example -t . -s ./test""" +linter = "flake8 ./src" +tests = """pytest ../tests/allure_nose2""" diff --git a/allure-nose2/src/plugin.py b/allure-nose2/src/plugin.py index 0e5f936f..c7f64608 100644 --- a/allure-nose2/src/plugin.py +++ b/allure-nose2/src/plugin.py @@ -47,7 +47,7 @@ class Allure(Plugin): commandLineSwitch = (None, "allure", "Generate an Allure report") def __init__(self, *args, **kwargs): - super(Allure, self).__init__(*args, **kwargs) + super().__init__(*args, **kwargs) self._host = host_tag() self._thread = thread_tag() self.lifecycle = AllureLifecycle() diff --git a/allure-nose2/test/example_loader.py b/allure-nose2/test/example_loader.py deleted file mode 100644 index e4e3f167..00000000 --- a/allure-nose2/test/example_loader.py +++ /dev/null @@ -1,29 +0,0 @@ -import sys -import types -from importlib import util -from doctest import script_from_examples -from nose2 import events - - -class CurrentExample(events.Plugin): - commandLineSwitch = (None, "current-example", "Method docstring to module") - - def __init__(self, *args, **kwargs): - super(CurrentExample, self).__init__(*args, **kwargs) - self._current_docstring = "" - - def startTest(self, event): - if hasattr(event.test, "_testFunc"): - self._current_docstring = event.test._testFunc.__doc__ - else: - self._current_docstring = event.test._testMethodDoc - - def get_example_module(self): - module = types.ModuleType("stub") - if self._current_docstring: - code = script_from_examples(self._current_docstring) - spec = util.spec_from_loader("example_module", origin="example_module", loader=None) - module = util.module_from_spec(spec) - exec(code, module.__dict__) - sys.modules['example_module'] = module - return module diff --git a/allure-nose2/test/example_runner.py b/allure-nose2/test/example_runner.py deleted file mode 100644 index eb2b5219..00000000 --- a/allure-nose2/test/example_runner.py +++ /dev/null @@ -1,62 +0,0 @@ -import io -import sys -from contextlib import ContextDecorator, redirect_stdout, redirect_stderr -from nose2 import main -from allure_commons import plugin_manager -from allure_commons.logger import AllureMemoryLogger -from allure_commons.logger import AllureFileLogger -from .example_loader import CurrentExample - -_Allure = sys.modules['allure_nose2.plugin'].Allure - - -class TestAllure(_Allure): - commandLineSwitch = (None, "test-allure", "Generate an Allure report") - - def register_allure_plugins(self): - self.fileLoger = AllureFileLogger("examples") - self.logger = AllureMemoryLogger() - plugin_manager.register(self.fileLoger) - plugin_manager.register(self.listener) - plugin_manager.register(self.logger) - - def unregister_allure_plugins(self): - plugin_manager.unregister(plugin=self.fileLoger) - plugin_manager.unregister(plugin=self.listener) - plugin_manager.unregister(plugin=self.logger) - - -def get_plugin(instance, plugin): - return next(iter([p for p in instance.getCurrentSession().plugins if isinstance(p, plugin)])) - - -class test_context(ContextDecorator): - def __enter__(self): - get_plugin(main, _Allure).unregister_allure_plugins() - - def __exit__(self, exc_type, exc_val, exc_tb): - get_plugin(main, _Allure).register_allure_plugins() - - -@test_context() -def run_docstring_example(**kwargs): - kwargs['exit'] = False - # kwargs['plugins'] = ["test.common", "nose2.plugins.mp"] - # kwargs['argv'] = ('nose2', '--test-allure', '--processes=2') - - kwargs['plugins'] = ["test.example_runner"] - kwargs['argv'] = ('nose2', '--test-allure') - - kwargs['module'] = get_plugin(main, CurrentExample).get_example_module() - - test_nose2 = type("TestNose2", (main,), {}) - stdout = io.StringIO() - stderr = io.StringIO() - - with redirect_stderr(stderr): - with redirect_stdout(stdout): - test_nose2_instance = test_nose2(**kwargs) - - # test_nose2_instance = test_nose2(**kwargs) - - return get_plugin(test_nose2_instance, _Allure).logger diff --git a/allure-nose2/test/labels/test_bdd_labels.py b/allure-nose2/test/labels/test_bdd_labels.py deleted file mode 100644 index 6277a893..00000000 --- a/allure-nose2/test/labels/test_bdd_labels.py +++ /dev/null @@ -1,85 +0,0 @@ -import unittest -from hamcrest import assert_that -from allure_commons_test.report import has_test_case -from allure_commons_test.label import has_epic -from allure_commons_test.label import has_feature -from test.example_runner import run_docstring_example - - -class TestBDDLabel(unittest.TestCase): - def test_method_label(self): - """ - >>> import unittest - >>> import allure - - >>> class TestBDDLabelExample(unittest.TestCase): - ... @allure.epic("Label", "Bdd") - ... @allure.feature("Method label") - ... def test_method_label_example(self): - ... pass - """ - allure_report = run_docstring_example() - assert_that(allure_report, - has_test_case("test_method_label_example", - has_epic("Label"), - has_epic("Bdd"), - has_feature("Method label") - ) - ) - - def test_class_label(self): - """ - >>> import unittest - >>> import allure - - >>> @allure.epic("Label", "Bdd") - ... class TestBDDLabelExample(unittest.TestCase): - ... def test_class_label_example(self): - ... pass - """ - allure_report = run_docstring_example() - assert_that(allure_report, - has_test_case("test_class_label_example", - has_epic("Label"), - has_epic("Bdd"), - ) - ) - - def test_class_method_label(self): - """ - >>> import unittest - >>> import allure - - >>> @allure.epic("Label", "Bdd") - ... class TestBDDLabelExample(unittest.TestCase): - ... @allure.feature("Method label") - ... def test_class_and_method_label_example(self): - ... pass - """ - allure_report = run_docstring_example() - assert_that(allure_report, - has_test_case("test_class_and_method_label_example", - has_epic("Label"), - has_epic("Bdd"), - has_feature("Method label") - ) - ) - - -def test_func_label(): - """ - >>> import allure - - >>> @allure.epic("Label", "Bdd") - ... @allure.feature("Function label") - ... def test_func_label_example(): - ... pass - """ - allure_report = run_docstring_example() - assert_that(allure_report, - has_test_case("test_func_label_example", - has_epic("Label"), - has_epic("Bdd"), - has_feature("Function label") - ) - ) diff --git a/allure-nose2/test/parametrized/test_parametrized.py b/allure-nose2/test/parametrized/test_parametrized.py deleted file mode 100644 index 1aff1fd1..00000000 --- a/allure-nose2/test/parametrized/test_parametrized.py +++ /dev/null @@ -1,59 +0,0 @@ -from nose2.tools import params -import unittest -from test.example_runner import run_docstring_example -from hamcrest import assert_that -from allure_commons_test.report import has_test_case -from allure_commons_test.result import has_parameter -from allure_commons.utils import represent - - -@params( - (("alpha", "hello"), ("betta", 42)), - (("alpha", "world"), ("betta", 777)) -) -def test_parametrized_func(first, second): - """ - >>> from nose2.tools import params - - >>> @params(("hello", 42), ("world", 777)) - ... def test_parametrized_func_example(alpha, betta): - ... pass - """ - first_param_name, first_param_value = first - second_param_name, second_param_value = second - - allure_report = run_docstring_example() - assert_that(allure_report, - has_test_case("test_parametrized_func_example", - has_parameter(first_param_name, represent(first_param_value)), - has_parameter(second_param_name, represent(second_param_value)) - ) - ) - - -class TestParametrized(unittest.TestCase): - - @params( - (("bravo", {"hello": 4}), ("charlie", [4, 2])), - (("bravo", {"wold": 2}), ("charlie", [7, 7, 7])) - ) - def test_parametrized_method(self, first, second): - """ - >>> import unittest - >>> from nose2.tools import params - - >>> class TestParametrizedExample(unittest.TestCase): - ... @params(({"hello": 4}, [4, 2]), ({"wold": 2}, [7, 7, 7])) - ... def test_parametrized_method_example(self, bravo, charlie): - ... pass - """ - first_param_name, first_param_value = first - second_param_name, second_param_value = second - - allure_report = run_docstring_example() - assert_that(allure_report, - has_test_case("test_parametrized_method_example", - has_parameter(first_param_name, represent(first_param_value)), - has_parameter(second_param_name, represent(second_param_value)) - ) - ) diff --git a/allure-nose2/test/result/test_fullname.py b/allure-nose2/test/result/test_fullname.py deleted file mode 100644 index f904a49c..00000000 --- a/allure-nose2/test/result/test_fullname.py +++ /dev/null @@ -1,39 +0,0 @@ -import unittest -from hamcrest import assert_that -from test.example_runner import run_docstring_example -from hamcrest import has_entry, has_item, has_property - - -def test_func_fullname(): - """ - >>> def test_func_fullname_example(): - ... pass - """ - allure_report = run_docstring_example() - assert_that(allure_report, - has_property("test_cases", - has_item( - has_entry("fullName", "example_module.test_func_fullname_example") - ) - ) - ) - - -class TestFullname(unittest.TestCase): - def test_method_fullname(self): - """ - >>> import unittest - - >>> class TestFullnameExample(unittest.TestCase): - ... def test_method_fullname_example(self): - ... pass - """ - allure_report = run_docstring_example() - assert_that(allure_report, - has_property("test_cases", - has_item( - has_entry("fullName", - "example_module.TestFullnameExample.test_method_fullname_example") - ) - ) - ) diff --git a/allure-nose2/test/result/test_status.py b/allure-nose2/test/result/test_status.py deleted file mode 100644 index 6cc6d059..00000000 --- a/allure-nose2/test/result/test_status.py +++ /dev/null @@ -1,71 +0,0 @@ -import unittest -from hamcrest import assert_that -from allure_commons_test.report import has_test_case -from allure_commons_test.result import with_status -from allure_commons_test.result import has_status_details -from allure_commons_test.result import with_message_contains -from test.example_runner import run_docstring_example - - -class TestStatus(unittest.TestCase): - def test_passed_status(self): - """ - >>> import unittest - - >>> class TestStatusExample(unittest.TestCase): - ... def test_passed_example(self): - ... assert True - """ - allure_report = run_docstring_example() - assert_that(allure_report, - has_test_case("test_passed_example", - with_status("passed")) - ) - - def test_failed_status(self): - """ - >>> import unittest - - >>> class TestStatusExample(unittest.TestCase): - ... def test_failed_example(self): - ... assert False, "my message" - """ - allure_report = run_docstring_example() - assert_that(allure_report, - has_test_case("test_failed_example", - with_status("failed"), - has_status_details(with_message_contains("my message")) - ) - ) - - def test_broken_status(self): - """ - >>> import unittest - - >>> class TestStatusExample(unittest.TestCase): - ... def test_broken_example(self): - ... raise Exception("my error") - """ - allure_report = run_docstring_example() - assert_that(allure_report, - has_test_case("test_broken_example", - with_status("broken"), - has_status_details(with_message_contains("my error")) - ) - ) - - def test_skipped_status(self): - """ - >>> import unittest - - >>> class TestStatusExample(unittest.TestCase): - ... def test_skipped_example(self): - ... self.skipTest('my skip reason') - """ - allure_report = run_docstring_example() - assert_that(allure_report, - has_test_case("test_skipped_example", - with_status("skipped"), - has_status_details(with_message_contains("my skip reason")) - ) - ) diff --git a/allure-pytest-bdd/examples/scenario-outline/outline.feature b/allure-pytest-bdd/examples/scenario-outline/outline.feature deleted file mode 100644 index 90291d08..00000000 --- a/allure-pytest-bdd/examples/scenario-outline/outline.feature +++ /dev/null @@ -1,10 +0,0 @@ -Feature: Allure report for scenario outline - Scenario Outline: Two examples with two parameters each - Given first step for value - When something is done with the value - Then check postconditions using and - - Examples: - | first | second | - | Alpha | 1 | - | Bravo | 2 | diff --git a/allure-pytest-bdd/examples/scenario-outline/scenario_outline_test.py b/allure-pytest-bdd/examples/scenario-outline/scenario_outline_test.py deleted file mode 100644 index b2fda514..00000000 --- a/allure-pytest-bdd/examples/scenario-outline/scenario_outline_test.py +++ /dev/null @@ -1,22 +0,0 @@ -from pytest_bdd import scenario, given, when, then -from pytest_bdd import parsers - - -@scenario("outline.feature", "Two examples with two parameters each") -def test_scenario_outline(): - pass - - -@given(parsers.parse("first step for {first} value")) -def given_first_step_for_first_value(first): - pass - - -@when(parsers.parse("something is done with the value {second}")) -def when_something_is_done_with_the_value_second(second): - pass - - -@then(parsers.parse("check postconditions using {first} and {second}")) -def then_check_postconditions_using_first_and_second(first, second): - pass diff --git a/allure-pytest-bdd/examples/simple-scenario/scenario.feature b/allure-pytest-bdd/examples/simple-scenario/scenario.feature deleted file mode 100644 index b06372c4..00000000 --- a/allure-pytest-bdd/examples/simple-scenario/scenario.feature +++ /dev/null @@ -1,5 +0,0 @@ -Feature: Basic allure-pytest-bdd usage - Scenario: Simple passed example - Given the preconditions are satisfied - When the action is invoked - Then the postconditions are held diff --git a/allure-pytest-bdd/examples/simple-scenario/scenario_test.py b/allure-pytest-bdd/examples/simple-scenario/scenario_test.py deleted file mode 100644 index ab66b592..00000000 --- a/allure-pytest-bdd/examples/simple-scenario/scenario_test.py +++ /dev/null @@ -1,21 +0,0 @@ -from pytest_bdd import scenario, given, when, then - - -@scenario("scenario.feature", "Simple passed example") -def test_scenario_passes(): - pass - - -@given("the preconditions are satisfied") -def given_the_preconditions_are_satisfied(): - pass - - -@when("the action is invoked") -def when_the_action_is_invoked(): - pass - - -@then("the postconditions are held") -def then_the_postconditions_are_held(): - pass diff --git a/allure-pytest-bdd/setup.py b/allure-pytest-bdd/setup.py index a97275d9..bf166279 100644 --- a/allure-pytest-bdd/setup.py +++ b/allure-pytest-bdd/setup.py @@ -34,7 +34,6 @@ def prepare_version(): configuration = {"root": "..", "relative_to": __file__} version = get_version(**configuration) install_requires.append(f"allure-python-commons=={version}") - install_requires.append(f"allure-pytest=={version}") return configuration diff --git a/allure-python-commons-test/src/result.py b/allure-python-commons-test/src/result.py index 3cd20591..c9c3d18e 100644 --- a/allure-python-commons-test/src/result.py +++ b/allure-python-commons-test/src/result.py @@ -93,19 +93,26 @@ def has_step(name, *matchers): ) -def has_parameter(name, value, *matchers): +def get_parameter_matcher(name, *matchers): return has_entry( 'parameters', has_item( all_of( has_entry('name', equal_to(name)), - has_entry('value', equal_to(value)), *matchers ) ) ) +def has_parameter(name, value, *matchers): + return get_parameter_matcher( + name, + has_entry('value', equal_to(value)), + *matchers + ) + + def doesnt_have_parameter(name): return has_entry('parameters', not_( diff --git a/allure-robotframework/pyproject.toml b/allure-robotframework/pyproject.toml index 41bf95b1..d869331e 100644 --- a/allure-robotframework/pyproject.toml +++ b/allure-robotframework/pyproject.toml @@ -1,5 +1,5 @@ [tool.poe.tasks] -linter = "flake8 ./src ./test" +linter = "flake8 ./src" tests = { shell = """python -m doctest ./src/listener/utils.py && pytest ../tests/allure_robotframework """ } diff --git a/tests/allure_behave/acceptance/allure_api/attachment/attachment_test.py b/tests/allure_behave/acceptance/allure_api/attachment/attachment_test.py index e3e082af..93029b0a 100644 --- a/tests/allure_behave/acceptance/allure_api/attachment/attachment_test.py +++ b/tests/allure_behave/acceptance/allure_api/attachment/attachment_test.py @@ -1,16 +1,17 @@ """ ./allure-behave/examples/attachment.rst """ -from tests.allure_behave.conftest import AllureBehaveRunner +from tests.allure_behave.behave_runner import AllureBehaveRunner from hamcrest import assert_that, equal_to from allure_commons_test.report import has_test_case from allure_commons_test.result import has_attachment_with_content from allure_commons_test.result import has_attachment from allure_commons_test.result import has_step + def test_data_attachment_from_step(behave_runner: AllureBehaveRunner): - behave_runner.run_rst_example( - "data-attachment-feature", - steps=["data-attachment-steps"] + behave_runner.run_behave( + feature_rst_ids=["data-attachment-feature"], + step_rst_ids=["data-attachment-steps"] ) assert_that( behave_runner.allure_results, @@ -41,9 +42,9 @@ def test_data_attachment_from_step(behave_runner: AllureBehaveRunner): def test_file_attachment_from_step(behave_runner: AllureBehaveRunner): - behave_runner.run_rst_example( - "file-attachment-feature", - steps=["file-attachment-steps"] + behave_runner.run_behave( + feature_rst_ids=["file-attachment-feature"], + step_rst_ids=["file-attachment-steps"] ) assert_that( behave_runner.allure_results, @@ -63,10 +64,10 @@ def test_data_attachment_from_hook(behave_runner: AllureBehaveRunner): Scenario: Attachment from after_scenario hook Given noop """ - behave_runner.run_rst_example( + behave_runner.run_behave( feature_literals=[feature], step_literals=["given('noop')(lambda _:0)"], - environment="attach-hook" + environment_rst_id="attach-hook" ) assert_that( behave_runner.allure_results, @@ -79,4 +80,4 @@ def test_data_attachment_from_hook(behave_runner: AllureBehaveRunner): name="attachment.txt" ) ) - ) \ No newline at end of file + ) diff --git a/tests/allure_behave/acceptance/allure_api/description/description_test.py b/tests/allure_behave/acceptance/allure_api/description/description_test.py index bcf26e9d..49152daa 100644 --- a/tests/allure_behave/acceptance/allure_api/description/description_test.py +++ b/tests/allure_behave/acceptance/allure_api/description/description_test.py @@ -1,15 +1,16 @@ """ ./allure-behave/examples/description.rst """ -from tests.allure_behave.conftest import AllureBehaveRunner +from tests.allure_behave.behave_runner import AllureBehaveRunner from hamcrest import assert_that from allure_commons_test.report import has_test_case from allure_commons_test.result import has_description from allure_commons_test.result import with_status + def test_descriptions_from_feature_file(behave_runner: AllureBehaveRunner): - behave_runner.run_rst_example( - "description-in-feature-feature", - steps=["description-in-feature-steps"] + behave_runner.run_behave( + feature_rst_ids=["description-in-feature-feature"], + step_rst_ids=["description-in-feature-steps"] ) assert_that( behave_runner.allure_results, @@ -25,9 +26,9 @@ def test_descriptions_from_feature_file(behave_runner: AllureBehaveRunner): def test_descriptions_from_step(behave_runner: AllureBehaveRunner): - behave_runner.run_rst_example( - "description-in-step-feature", - steps=["description-in-step-steps"] + behave_runner.run_behave( + feature_rst_ids=["description-in-step-feature"], + step_rst_ids=["description-in-step-steps"] ) assert_that( behave_runner.allure_results, @@ -43,10 +44,10 @@ def test_descriptions_from_step(behave_runner: AllureBehaveRunner): def test_descriptions_before_scenario(behave_runner: AllureBehaveRunner): - behave_runner.run_rst_example( - "description-in-hook-feature", - steps=["description-in-feature-steps"], - environment="description-in-hook-env" + behave_runner.run_behave( + feature_rst_ids=["description-in-hook-feature"], + step_rst_ids=["description-in-feature-steps"], + environment_rst_id="description-in-hook-env" ) assert_that( behave_runner.allure_results, @@ -61,10 +62,10 @@ def test_descriptions_before_scenario(behave_runner: AllureBehaveRunner): def test_descriptions_after_scenario(behave_runner: AllureBehaveRunner): - behave_runner.run_rst_example( - "description-in-hook-feature", - steps=["description-in-feature-steps"], - environment="description-in-hook-env" + behave_runner.run_behave( + feature_rst_ids=["description-in-hook-feature"], + step_rst_ids=["description-in-feature-steps"], + environment_rst_id="description-in-hook-env" ) assert_that( behave_runner.allure_results, diff --git a/tests/allure_behave/acceptance/allure_api/labels/label_test.py b/tests/allure_behave/acceptance/allure_api/labels/label_test.py index 4e5a9d3c..3bbf0022 100644 --- a/tests/allure_behave/acceptance/allure_api/labels/label_test.py +++ b/tests/allure_behave/acceptance/allure_api/labels/label_test.py @@ -1,14 +1,15 @@ """ ./allure-behave/examples/label.rst """ -from tests.allure_behave.conftest import AllureBehaveRunner +from tests.allure_behave.behave_runner import AllureBehaveRunner from hamcrest import assert_that from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.label import has_label + def test_label_from_feature_file(behave_runner: AllureBehaveRunner): - behave_runner.run_rst_example( - "label-feature", + behave_runner.run_behave( + feature_rst_ids=["label-feature"], step_literals=["given('noop')(lambda c:None)"] ) assert_that( diff --git a/tests/allure_behave/acceptance/allure_api/links/link_test.py b/tests/allure_behave/acceptance/allure_api/links/link_test.py index ddb3d12e..c9628b98 100644 --- a/tests/allure_behave/acceptance/allure_api/links/link_test.py +++ b/tests/allure_behave/acceptance/allure_api/links/link_test.py @@ -1,6 +1,6 @@ """ ./allure-behave/examples/link.rst """ -from tests.allure_behave.conftest import AllureBehaveRunner +from tests.allure_behave.behave_runner import AllureBehaveRunner from hamcrest import assert_that, all_of from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status @@ -8,9 +8,10 @@ from allure_commons_test.result import has_issue_link from allure_commons_test.result import has_test_case_link + def test_link_on_scenario_level(behave_runner: AllureBehaveRunner): - behave_runner.run_rst_example( - "link-scenario-feature", + behave_runner.run_behave( + feature_rst_ids=["link-scenario-feature"], step_literals=["given('noop')(lambda c:None)"] ) assert_that( @@ -24,8 +25,8 @@ def test_link_on_scenario_level(behave_runner: AllureBehaveRunner): def test_link_on_feature_level(behave_runner: AllureBehaveRunner): - behave_runner.run_rst_example( - "link-feature-feature", + behave_runner.run_behave( + feature_rst_ids=["link-feature-feature"], step_literals=["given('noop')(lambda c:None)"] ) assert_that( @@ -44,9 +45,10 @@ def test_link_on_feature_level(behave_runner: AllureBehaveRunner): ) ) + def test_specialized_links(behave_runner: AllureBehaveRunner): - behave_runner.run_rst_example( - "specialized-links-feature", + behave_runner.run_behave( + feature_rst_ids=["specialized-links-feature"], step_literals=["given('noop')(lambda c:None)"] ) assert_that( @@ -73,10 +75,10 @@ def test_specialized_links(behave_runner: AllureBehaveRunner): def test_dynamic_links(behave_runner: AllureBehaveRunner): - behave_runner.run_rst_example( - "dynamic-links-feature", - steps=["dynamic-links-steps"], - environment="dynamic-links-hooks" + behave_runner.run_behave( + feature_rst_ids=["dynamic-links-feature"], + step_rst_ids=["dynamic-links-steps"], + environment_rst_id="dynamic-links-hooks" ) assert_that( behave_runner.allure_results, @@ -86,12 +88,11 @@ def test_dynamic_links(behave_runner: AllureBehaveRunner): has_issue_link( "https://github.com/allure-framework/allure-python/issues/1", "Skip None and empty values in json" - ) - , + ), has_link( "https://qameta.io/allure-report/", "link", "Allure Report" ) ) - ) \ No newline at end of file + ) diff --git a/tests/allure_behave/acceptance/allure_api/severities/severity_test.py b/tests/allure_behave/acceptance/allure_api/severities/severity_test.py index de9aec5f..9ad562bd 100644 --- a/tests/allure_behave/acceptance/allure_api/severities/severity_test.py +++ b/tests/allure_behave/acceptance/allure_api/severities/severity_test.py @@ -1,7 +1,7 @@ """ ./allure-behave/examples/severity.rst """ import pytest -from tests.allure_behave.conftest import AllureBehaveRunner +from tests.allure_behave.behave_runner import AllureBehaveRunner from hamcrest import assert_that, all_of from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status @@ -16,8 +16,8 @@ pytest.param("Trivial scenario", "trivial", id="trivial") ]) def test_severity_on_scenario(name, sev, behave_runner: AllureBehaveRunner): - behave_runner.run_rst_example( - "severity-on-scenario", + behave_runner.run_behave( + feature_rst_ids=["severity-on-scenario"], step_literals=["given('noop')(lambda c:None)"] ) assert_that( @@ -31,8 +31,8 @@ def test_severity_on_scenario(name, sev, behave_runner: AllureBehaveRunner): def test_severity_on_feature(behave_runner: AllureBehaveRunner): - behave_runner.run_rst_example( - "severity-on-feature", + behave_runner.run_behave( + feature_rst_ids=["severity-on-feature"], step_literals=["given('noop')(lambda c:None)"] ) assert_that( @@ -53,8 +53,8 @@ def test_severity_on_feature(behave_runner: AllureBehaveRunner): def test_multiple_severity_tags(behave_runner: AllureBehaveRunner): - behave_runner.run_rst_example( - "multiple-severities", + behave_runner.run_behave( + feature_rst_ids=["multiple-severities"], step_literals=["given('noop')(lambda c:None)"] ) assert_that( diff --git a/tests/allure_behave/acceptance/allure_api/tags/tag_test.py b/tests/allure_behave/acceptance/allure_api/tags/tag_test.py index d077aa95..6c9b3e6a 100644 --- a/tests/allure_behave/acceptance/allure_api/tags/tag_test.py +++ b/tests/allure_behave/acceptance/allure_api/tags/tag_test.py @@ -1,7 +1,7 @@ """ ./allure-behave/examples/tag.rst """ import pytest -from tests.allure_behave.conftest import AllureBehaveRunner +from tests.allure_behave.behave_runner import AllureBehaveRunner from hamcrest import assert_that from allure_commons_test.report import has_test_case from allure_commons_test.label import has_tag @@ -33,8 +33,8 @@ def test_behave_tags_as_allure_tags( tags, behave_runner: AllureBehaveRunner ): - behave_runner.run_rst_example( - feature_id, + behave_runner.run_behave( + feature_rst_ids=[feature_id], step_literals=["given('noop')(lambda c:None)"] ) assert_that( @@ -43,4 +43,4 @@ def test_behave_tags_as_allure_tags( scenario, *(has_tag(tag) for tag in tags) ) - ) \ No newline at end of file + ) diff --git a/tests/allure_behave/acceptance/allure_api/testplan/testplan_test.py b/tests/allure_behave/acceptance/allure_api/testplan/testplan_test.py index 825c2174..444d8b7d 100644 --- a/tests/allure_behave/acceptance/allure_api/testplan/testplan_test.py +++ b/tests/allure_behave/acceptance/allure_api/testplan/testplan_test.py @@ -3,29 +3,14 @@ from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from hamcrest import assert_that, all_of, not_ -from pytest import MonkeyPatch -from tests.allure_behave.conftest import AllureBehaveRunner -from tests.conftest import RstExampleTable +from tests.allure_behave.behave_runner import AllureBehaveRunner -def test_testplan_fullname_selection( - monkeypatch: MonkeyPatch, - rst_examples: RstExampleTable, - behave_runner: AllureBehaveRunner -): - monkeypatch.setenv( - "ALLURE_TESTPLAN_PATH", - str( - behave_runner.pytester.makefile( - ".json", - rst_examples["fullname-testplan"] - ) - ) - ) - behave_runner.run_rst_example( - "fullname-feature-1", - "fullname-feature-2", - steps=["steps"] +def test_testplan_fullname_selection(behave_runner: AllureBehaveRunner): + behave_runner.run_behave( + feature_rst_ids=["fullname-feature-1", "fullname-feature-2"], + step_rst_ids=["steps"], + testplan_rst_id="fullname-testplan" ) assert_that( @@ -51,25 +36,11 @@ def test_testplan_fullname_selection( ) -def test_testplan_id_selection( - monkeypatch: MonkeyPatch, - rst_examples: RstExampleTable, - behave_runner: AllureBehaveRunner -): - monkeypatch.setenv( - "ALLURE_TESTPLAN_PATH", - str( - behave_runner.pytester.makefile( - ".json", - rst_examples["id-testplan"] - ) - ) - ) - - behave_runner.run_rst_example( - "id-feature-1", - "id-feature-2", - steps=["steps"] +def test_testplan_id_selection(behave_runner: AllureBehaveRunner): + behave_runner.run_behave( + feature_rst_ids=["id-feature-1", "id-feature-2"], + step_rst_ids=["steps"], + testplan_rst_id="id-testplan" ) assert_that( @@ -95,26 +66,12 @@ def test_testplan_id_selection( ) -def test_skipping_of_tests_missing_in_testplan( - monkeypatch: MonkeyPatch, - rst_examples: RstExampleTable, - behave_runner: AllureBehaveRunner -): - monkeypatch.setenv( - "ALLURE_TESTPLAN_PATH", - str( - behave_runner.pytester.makefile( - ".json", - rst_examples["fullname-testplan"] - ) - ) - ) - - behave_runner.run_rst_example( - "fullname-feature-1", - "fullname-feature-2", - steps=["steps"], - cli_args=["-D", "AllureFormatter.hide_excluded=True"] +def test_skipping_of_tests_missing_in_testplan(behave_runner: AllureBehaveRunner): + behave_runner.run_behave( + feature_rst_ids=["fullname-feature-1", "fullname-feature-2"], + step_rst_ids=["steps"], + testplan_rst_id="fullname-testplan", + options=["-D", "AllureFormatter.hide_excluded=True"] ) assert_that( diff --git a/tests/allure_behave/acceptance/behave_support/background/background_test.py b/tests/allure_behave/acceptance/behave_support/background/background_test.py index d1357468..0a3a0eee 100644 --- a/tests/allure_behave/acceptance/behave_support/background/background_test.py +++ b/tests/allure_behave/acceptance/behave_support/background/background_test.py @@ -1,6 +1,6 @@ import pytest from hamcrest import assert_that, all_of -from tests.allure_behave.conftest import AllureBehaveRunner +from tests.allure_behave.behave_runner import AllureBehaveRunner from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.result import has_step @@ -16,13 +16,14 @@ ] ) def test_background( + docstring: str, behave_runner: AllureBehaveRunner, step_outcome: str, status: str, remained_steps_status: str ): - feature = \ - F"""Feature: Allure-behave compatibility with feature backgrounds + """ + Feature: Allure-behave compatibility with feature backgrounds Background: A background with {step_outcome} step Given the first background step that is {step_outcome} And the second background step with no failures @@ -35,7 +36,9 @@ def test_background( Given the step with no failures """ behave_runner.run_behave( - features=[feature], + feature_literals=[ + docstring.format(step_outcome=step_outcome) + ], step_paths=["./background_steps.py"] ) assert_that( @@ -78,4 +81,4 @@ def test_background( ) ) ) - ) \ No newline at end of file + ) diff --git a/tests/allure_behave/acceptance/behave_support/behave_cmd/behave_cmd_test.py b/tests/allure_behave/acceptance/behave_support/behave_cmd/behave_cmd_test.py index 5a157481..6e3fe849 100644 --- a/tests/allure_behave/acceptance/behave_support/behave_cmd/behave_cmd_test.py +++ b/tests/allure_behave/acceptance/behave_support/behave_cmd/behave_cmd_test.py @@ -1,8 +1,9 @@ from hamcrest import assert_that, all_of, not_ -from tests.allure_behave.conftest import AllureBehaveRunner +from tests.allure_behave.behave_runner import AllureBehaveRunner from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status + def test_behave_tags_filter(docstring: str, behave_runner: AllureBehaveRunner): """Feature: Behave --tags CLI argument support @@ -15,9 +16,9 @@ def test_behave_tags_filter(docstring: str, behave_runner: AllureBehaveRunner): """ behave_runner.run_behave( - features=[docstring], - steps=["given('noop')(lambda c:None)"], - cli_args=["--tags=tag"] + feature_literals=[docstring], + step_literals=["given('noop')(lambda c:None)"], + options=["--tags=tag"] ) assert_that( behave_runner.allure_results, @@ -45,9 +46,9 @@ def test_behave_no_skipped_support(docstring: str, behave_runner: AllureBehaveRu Given noop """ behave_runner.run_behave( - features=[docstring], - steps=["given('noop')(lambda c:None)"], - cli_args=["--tags=tag", "--no-skipped"] + feature_literals=[docstring], + step_literals=["given('noop')(lambda c:None)"], + options=["--tags=tag", "--no-skipped"] ) assert_that( behave_runner.allure_results, diff --git a/tests/allure_behave/acceptance/behave_support/hooks/hook_test.py b/tests/allure_behave/acceptance/behave_support/hooks/hook_test.py index 7054778e..197ed5a1 100644 --- a/tests/allure_behave/acceptance/behave_support/hooks/hook_test.py +++ b/tests/allure_behave/acceptance/behave_support/hooks/hook_test.py @@ -1,5 +1,5 @@ import allure -from tests.allure_behave.conftest import AllureBehaveRunner as Runner +from tests.allure_behave.behave_runner import AllureBehaveRunner as Runner from hamcrest import assert_that, all_of, not_, equal_to from allure_commons_test.container import has_container, has_before, has_after from allure_commons_test.report import has_test_case @@ -54,6 +54,7 @@ def test_global_hooks(behave_runner: Runner): ) ) + def test_tag_hooks(behave_runner: Runner): behave_runner.run_behave( feature_paths=[ @@ -109,6 +110,7 @@ def test_tag_hooks(behave_runner: Runner): ) ) + def test_attachment_before_feature(behave_runner: Runner): behave_runner.run_behave( feature_paths=["./test-data/attachment-hook.feature"], @@ -227,4 +229,4 @@ def test_func_step_in_scenario_hooks(behave_runner: Runner): ) ) ) - ) \ No newline at end of file + ) diff --git a/tests/allure_behave/acceptance/behave_support/hooks/test-data/context-step-hooks.py b/tests/allure_behave/acceptance/behave_support/hooks/test-data/context-step-hooks.py index 689454e1..215add9b 100644 --- a/tests/allure_behave/acceptance/behave_support/hooks/test-data/context-step-hooks.py +++ b/tests/allure_behave/acceptance/behave_support/hooks/test-data/context-step-hooks.py @@ -6,7 +6,6 @@ def before_scenario(context, scenario): with allure.step("Step in before_scenario"): pass - @allure_commons.fixture def after_scenario(context, scenario): with allure.step("Step in after_scenario"): diff --git a/tests/allure_behave/acceptance/behave_support/scenario_outlines/scenario_outline_test.py b/tests/allure_behave/acceptance/behave_support/scenario_outlines/scenario_outline_test.py index 1bc8a49f..e6e73593 100644 --- a/tests/allure_behave/acceptance/behave_support/scenario_outlines/scenario_outline_test.py +++ b/tests/allure_behave/acceptance/behave_support/scenario_outlines/scenario_outline_test.py @@ -1,14 +1,15 @@ from hamcrest import assert_that, all_of, has_entry -from tests.allure_behave.conftest import AllureBehaveRunner +from tests.allure_behave.behave_runner import AllureBehaveRunner from allure_commons_test.report import has_only_testcases from allure_commons_test.result import with_status from allure_commons_test.result import has_parameter + def test_outline_with_single_table(behave_runner: AllureBehaveRunner): behave_runner.run_behave( feature_paths=["./test-data/outline.feature"], step_paths=["./test-data/steps.py"], - cli_args=["--tags=1", "--no-skipped"] + options=["--tags=1", "--no-skipped"] ) assert_that( @@ -40,7 +41,7 @@ def test_outline_with_multiple_tables(behave_runner: AllureBehaveRunner): behave_runner.run_behave( feature_paths=["./test-data/outline.feature"], step_paths=["./test-data/steps.py"], - cli_args=["--tags=multiple-tables", "--no-skipped"] + options=["--tags=multiple-tables", "--no-skipped"] ) assert_that( @@ -90,7 +91,7 @@ def test_multiple_outlines_each_with_one_table(behave_runner: AllureBehaveRunner behave_runner.run_behave( feature_paths=["./test-data/outline.feature"], step_paths=["./test-data/steps.py"], - cli_args=["--tags=single-table", "--no-skipped"] + options=["--tags=single-table", "--no-skipped"] ) assert_that( diff --git a/tests/allure_behave/acceptance/behave_support/scenarios/scenario_test.py b/tests/allure_behave/acceptance/behave_support/scenarios/scenario_test.py index 28ad6ff9..28b3f696 100644 --- a/tests/allure_behave/acceptance/behave_support/scenarios/scenario_test.py +++ b/tests/allure_behave/acceptance/behave_support/scenarios/scenario_test.py @@ -1,7 +1,6 @@ import pytest -from textwrap import dedent from hamcrest import assert_that -from tests.allure_behave.conftest import AllureBehaveRunner +from tests.allure_behave.behave_runner import AllureBehaveRunner from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.result import has_step @@ -42,8 +41,8 @@ def test_scenario_with_one_step( """ behave_runner.run_behave( - features=[docstring], - steps=[step] + feature_literals=[docstring], + step_literals=[step] ) assert_that( behave_runner.allure_results, @@ -91,8 +90,8 @@ def test_when_not_passed_remaining_steps_are_skipped( """ behave_runner.run_behave( - features=[docstring], - steps=[ + feature_literals=[docstring], + step_literals=[ "given('step {n}')(lambda c,**_:None)", trigger_step ] @@ -130,8 +129,8 @@ def test_nameless_scenario(docstring, behave_runner: AllureBehaveRunner): """ behave_runner.run_behave( - features=[docstring], - steps=["given('noop')(lambda c:None)"] + feature_literals=[docstring], + step_literals=["given('noop')(lambda c:None)"] ) assert_that( @@ -140,4 +139,4 @@ def test_nameless_scenario(docstring, behave_runner: AllureBehaveRunner): "Scenario", with_status("passed") ) - ) \ No newline at end of file + ) diff --git a/tests/allure_behave/acceptance/behave_support/steps/behave_step_test.py b/tests/allure_behave/acceptance/behave_support/steps/behave_step_test.py index 0cae4b70..1a601053 100644 --- a/tests/allure_behave/acceptance/behave_support/steps/behave_step_test.py +++ b/tests/allure_behave/acceptance/behave_support/steps/behave_step_test.py @@ -1,6 +1,6 @@ -import allure -from tests.allure_behave.conftest import AllureBehaveRunner +from tests.allure_behave.behave_runner import AllureBehaveRunner from hamcrest import assert_that, equal_to + from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.result import has_step @@ -9,7 +9,8 @@ from allure_commons_test.result import has_attachment_with_content from allure_commons_test.content import csv_equivalent -def test_failed_behave_step(docstring:str, behave_runner: AllureBehaveRunner): + +def test_failed_behave_step(docstring: str, behave_runner: AllureBehaveRunner): """ Feature: Bheave step support Scenario: Scenario with failed step @@ -17,8 +18,8 @@ def test_failed_behave_step(docstring:str, behave_runner: AllureBehaveRunner): """ behave_runner.run_behave( - features=[docstring], - steps=["@given('a step failed')\ndef _(_):assert False,'Fail message'"] + feature_literals=[docstring], + step_literals=["@given('a step failed')\ndef _(_):assert False,'Fail message'"] ) assert_that( @@ -36,7 +37,8 @@ def test_failed_behave_step(docstring:str, behave_runner: AllureBehaveRunner): ) ) -def test_broken_behave_step(docstring:str, behave_runner: AllureBehaveRunner): + +def test_broken_behave_step(docstring, behave_runner: AllureBehaveRunner): """ Feature: Bheave step support Scenario: Scenario with broken step @@ -44,8 +46,8 @@ def test_broken_behave_step(docstring:str, behave_runner: AllureBehaveRunner): """ behave_runner.run_behave( - features=[docstring], - steps=["@given('a broken step')\ndef _(_):raise ValueError('Reason')"] + feature_literals=[docstring], + step_literals=["@given('a broken step')\ndef _(_):raise ValueError('Reason')"] ) assert_that( @@ -64,7 +66,7 @@ def test_broken_behave_step(docstring:str, behave_runner: AllureBehaveRunner): ) -def test_step_text_data(docstring:str, behave_runner: AllureBehaveRunner): +def test_step_text_data(docstring, behave_runner: AllureBehaveRunner): """ Feature: Bheave step support Scenario: Scenario with step which contains text data @@ -75,8 +77,8 @@ def test_step_text_data(docstring:str, behave_runner: AllureBehaveRunner): """ behave_runner.run_behave( - features=[docstring], - steps=["given('a step with text data')(lambda _:0)"] + feature_literals=[docstring], + step_literals=["given('a step with text data')(lambda _:0)"] ) assert_that( @@ -97,7 +99,7 @@ def test_step_text_data(docstring:str, behave_runner: AllureBehaveRunner): ) -def test_step_table_data(docstring:str, behave_runner: AllureBehaveRunner): +def test_step_table_data(docstring, behave_runner: AllureBehaveRunner): """ Feature: Bheave step support Scenario: Scenario with step which contains a table @@ -108,8 +110,8 @@ def test_step_table_data(docstring:str, behave_runner: AllureBehaveRunner): """ behave_runner.run_behave( - features=[docstring], - steps=["given('a step with table data')(lambda _:0)"] + feature_literals=[docstring], + step_literals=["given('a step with table data')(lambda _:0)"] ) assert_that( diff --git a/tests/allure_behave/behave_runner.py b/tests/allure_behave/behave_runner.py new file mode 100644 index 00000000..bb126916 --- /dev/null +++ b/tests/allure_behave/behave_runner.py @@ -0,0 +1,195 @@ +import behave.step_registry +import sys + +from behave import matchers +from behave.configuration import Configuration +from behave.formatter.base import StreamOpener +from behave.formatter.pretty import PrettyFormatter +from behave.parser import parse_feature +from behave.runner import Context, Runner +from behave.step_registry import setup_step_decorators +from behave.step_registry import StepRegistry +from pathlib import Path +from pytest import FixtureRequest, Pytester +from typing import Sequence +from tests.e2e import AllureFrameworkRunner + +from allure_behave.formatter import AllureFormatter + + +def __fix_behave_in_memory_run(): + # Behave has poor support for consecutive prigrammatic runs. This is due to + # how step decorators are cached. + # There are three ways to introduce behave step decorators (i.e., @given) + # into a step definition module: + # 1. from behave import given (most common use) + # 2. Without any import (just as if they are globally defined, less + # common) + # 3. from behave.step_regostry import given (rarely) + # The decorators are associated with a StepRegistry instance. This + # association is first created when behave.step_registry module is imported. + # They are then introduced as the behave package attributes and basically + # are cached on a package level. Even if we replace the decorators from + # behave.step_registry with new ones associated with a new StepRegistry with + # the behave.step_registry.setup_step_decorators function, the decorators in + # the behave package remains the same and remains attached to the old step + # registry. + # We need to create a new StepRegistry before a run to prevent step + # duplication error, but if step definitions are created with the decorators + # introduced with (1), they will be added to the old registry and will be + # never matched. + # To fix that we force decorators to use the global instance of the + # StepRegistry. + original_add_step_definition = StepRegistry.add_step_definition + + def __fixed_add_step_definition(self, *args, **kwargs): + return original_add_step_definition( + behave.step_registry.registry, + *args, + **kwargs + ) + + StepRegistry.add_step_definition = __fixed_add_step_definition + + +class _InMemoryBehaveRunner(Runner): + def __init__(self, features, steps, environment, args=None): + if args is None: + args = [] + config = Configuration( + ["--no-snippets"] + list(args), + load_config=False + ) + super().__init__(config) + self.__features = features + self.__steps = steps + self.__environment = environment + + def load_hooks(self, filename=None): + if self.__environment: + exec(self.__environment, self.hooks) + if "before_all" not in self.hooks: + self.hooks["before_all"] = self.before_all_default_hook + + def load_step_definitions(self, extra_step_paths=None): + behave.step_registry.registry = self.step_registry = StepRegistry() + step_globals = { + "use_step_matcher": matchers.use_step_matcher, + "step_matcher": matchers.step_matcher, + } + + # To support the decorators (i.e., @given) with no imports + setup_step_decorators(step_globals, self.step_registry) + + default_matcher = matchers.current_matcher + for step in self.__steps: + step_module_globals = step_globals.copy() + exec(step, step_module_globals) + matchers.current_matcher = default_matcher + + def load_features(self): + self.features.extend( + parse_feature(f) for f in self.__features + ) + + def load_formatter(self): + opener = StreamOpener(stream=sys.stdout) + allure_formatter = AllureFormatter(opener, self.config) + pretty_formatter = PrettyFormatter(opener, self.config) + self.formatters.append(allure_formatter) + self.formatters.append(pretty_formatter) + + def run(self): + self.context = Context(self) + self.load_hooks() + self.load_step_definitions() + self.load_features() + self.load_formatter() + self.run_model() + + +class AllureBehaveRunner(AllureFrameworkRunner): + """The runner to test allure-behave integration.""" + + LOGGER_PATH = "allure_behave.formatter.AllureFileLogger" + + def __init__(self, request: FixtureRequest, pytester: Pytester): + super().__init__(request, pytester) + + def run_behave( + self, + feature_paths: Sequence[str | Path] = None, + feature_literals: Sequence[str] = None, + feature_rst_ids: Sequence[str] = None, + step_paths: Sequence[str | Path] = None, + step_literals: Sequence[str] = None, + step_rst_ids: Sequence[str] = None, + environment_path: str | Path = None, + environment_literal: str = None, + environment_rst_id: str = None, + testplan_content: dict = None, + testplan_path: str | Path = None, + testplan_rst_id: str = None, + options: Sequence[str] = None, + ): + """Executes behave in memory and returns the allure results of the run. + + Arguments: + feature_paths (Sequence[str | Path]): paths to feature files + relative to the current test module folder. + feature_literals (Sequence[str]): content of feature files. + feature_rst_ids (Sequence[str]): IDs of .rst code blocks containing + feature file content. + step_paths (Sequence[str | Path]): paths to step definition files + relative to the current test module folder. + step_literals (Sequence[str]): content of step definition files. + step_rst_ids (Sequence[str]): IDs of .rst code blocks containing + step definitions. + environment_path (str | Path): a path to en environment file + relative to the current test module folder. + environment_literal (str): content of an environment file. + environment_rst_id (str): an ID of a .rst code block containing an + environment file content. + testplan_content (dict): an allure testplan content. + testplan_path (str | Path): a path to an allure testplan file + relative to the current test module folder. + testplan_rst_id (str): an ID of a .rst code block containing an + allure testplan. + options (Sequence[str]): behave CLI options + + Returns: + allure_commons.logger.AllureMemoryLogger: the allure results of the + run. The results of the last run are also accessible through the + :attr:`allure_results` attribute. + + """ + return self._run( + self._get_all_content( + paths=feature_paths, + literals=feature_literals, + rst_ids=feature_rst_ids + ), + self._get_all_content( + paths=step_paths, + literals=step_literals, + rst_ids=step_rst_ids + ), + self._resolve_content( + path=environment_path, + literal=environment_literal, + rst_id=environment_rst_id + ), + testplan_content=testplan_content, + testplan_path=testplan_path, + testplan_rst_id=testplan_rst_id, + logger_path=AllureBehaveRunner.LOGGER_PATH, + options=options + ) + + def _run_framework(self, features, steps, environment, options): + _InMemoryBehaveRunner(features, steps, environment, options).run() + + +__fix_behave_in_memory_run() + +__all__ = ["AllureBehaveRunner"] diff --git a/tests/allure_behave/conftest.py b/tests/allure_behave/conftest.py index 2aa1b4be..d0a99e13 100644 --- a/tests/allure_behave/conftest.py +++ b/tests/allure_behave/conftest.py @@ -1,258 +1,7 @@ -import sys -import behave.step_registry -from itertools import chain -from pathlib import Path -from pytest import FixtureRequest, fixture, Pytester -from tests.conftest import fake_logger -from tests.conftest import RstExampleTable -from behave.runner import Runner -from behave.step_registry import StepRegistry -from typing import Sequence -from behave.configuration import Configuration -from behave.formatter.base import StreamOpener -from behave.runner import Context, Runner -from behave import matchers -from behave.step_registry import setup_step_decorators -from behave.parser import parse_feature -from behave.formatter.pretty import PrettyFormatter -from allure_behave.formatter import AllureFormatter - -def __fix_behave_in_memory_run(): - # Behave has poor support for consecutive prigrammatic runs. This is due to - # how step decorators are cached. - # There are three ways to introduce behave step decorators (i.e., @given) - # into a step definition module: - # 1. from behave import given (most common use) - # 2. Without any import (just as if they are globally defined, less - # common) - # 3. from behave.step_regostry import given (rarely) - # The decorators are associated with a StepRegistry instance. This - # association is first created when behave.step_registry module is imported. - # They are then introduced as the behave package attributes and basically - # are cached on a package level. Even if we replace the decorators from - # behave.step_registry with new ones associated with a new StepRegistry with - # the behave.step_registry.setup_step_decorators function, the decorators in - # the behave package remains the same and remains attached to the old step - # registry. - # We need to create a new StepRegistry before a run to prevent step - # duplication error, but if step definitions are created with the decorators - # introduced with (1), they will be added to the old registry and will be - # never matched. - # To fix that we force decorators to use the global instance of the - # StepRegistry. - original_add_step_definition = StepRegistry.add_step_definition - def __fixed_add_step_definition(self, *args, **kwargs): - return original_add_step_definition( - behave.step_registry.registry, - *args, - **kwargs - ) - StepRegistry.add_step_definition = __fixed_add_step_definition - -class _InMemoryBehaveRunner(Runner): - def __init__(self, features, steps, environment, args=None): - if args is None: - args = [] - config = Configuration( - ["--no-snippets"] + list(args), - load_config=False - ) - super().__init__(config) - self.__features = features - self.__steps = steps - self.__environment = environment - - def load_hooks(self, filename=None): - if self.__environment: - exec(self.__environment, self.hooks) - if "before_all" not in self.hooks: - self.hooks["before_all"] = self.before_all_default_hook - - def load_step_definitions(self, extra_step_paths=None): - behave.step_registry.registry = self.step_registry = StepRegistry() - step_globals = { - "use_step_matcher": matchers.use_step_matcher, - "step_matcher": matchers.step_matcher, - } - - # To support the decorators (i.e., @given) with no imports - setup_step_decorators(step_globals, self.step_registry) - - default_matcher = matchers.current_matcher - for step in self.__steps: - step_module_globals = step_globals.copy() - exec(step, step_module_globals) - matchers.current_matcher = default_matcher - - def load_features(self): - self.features.extend( - parse_feature(f) for f in self.__features - ) - - def load_formatter(self): - opener = StreamOpener(stream=sys.stdout) - allure_formatter = AllureFormatter(opener, self.config) - pretty_formatter = PrettyFormatter(opener, self.config) - self.formatters.append(allure_formatter) - self.formatters.append(pretty_formatter) - - def run(self): - self.context = Context(self) - self.load_hooks() - self.load_step_definitions() - self.load_features() - self.load_formatter() - self.run_model() - - -class AllureBehaveRunner: - def __init__(self, pytester: Pytester, request: FixtureRequest): - self.pytester = pytester - self.request = request - self.exit_code = None - self.allure_results = None - - def run_behave( - self, - *, - features: Sequence[str] = None, - steps: Sequence[str] = None, - environment: str = None, - feature_paths: Sequence[str|Path] = None, - step_paths: Sequence[str|Path] = None, - environment_path: str|Path = None, - cli_args: Sequence[str] = None - ) -> None: - """Runs behave against specific set of features, steps and an - environment each specified either as a string literal or as a path to a - file. - - Arguments: - features (Sequence[str]): a sequence of strings each representing - a .feature file content. - steps (Sequence[str]): a sequence of strings each representing - content of a step definition file. - environment (str): a string representing content of the - environment.py file. - feature_paths (Sequence[str|Path]): a sequence of feature files. - step_paths (Sequence[str|Path]): a sequence of step definition - files. - environment_path (str|Path): a path to the environment.py file. - cli_args (Sequence[str]): a CLI arguments passed to behave. - - The result of the run is accessible through the :code:`allure_results` - attribute. - """ - - path_to_fake = "allure_behave.formatter.AllureFileLogger" - testdir = self.request.path.parent - features = self.__extend_content_seq(testdir, features, feature_paths) - steps = self.__extend_content_seq(testdir, steps, step_paths) - environment = self.__resolve_content( - testdir, - environment, - environment_path - ) - with fake_logger(path_to_fake) as allure_results: - _InMemoryBehaveRunner(features, steps, environment, cli_args).run() - self.allure_results = allure_results - - def run_rst_example( - self, - *features: str, - steps: Sequence[str] = None, - feature_literals: Sequence[str] = None, - step_literals: Sequence[str] = None, - environment: str = None, - environment_literal: str = None, - cli_args: Sequence[str] = None - ) -> None: - """Loads code blocks from reStructuredText document and executes behave - against them. - - Arguments: - *features (str): names of the feature code blocks - - Keyword arguments: - feature_literals (Sequence[str]): a sequence of strings, each - representing a .feature file content. - steps (Sequence[str]): names of the code blocks defining steps. - step_literals (Sequence[str]): a sequence of strings, each - representing a step definition file content. - environment (str): an optional name of the environment.py code - block. - environment_literal (str): an optional string, representing the - environment.py file content. - - Note: - See :class:`RstExampleTable` for more details. - - Note: - Uses in-memory execution model. See :meth:`run_behave_in_memory` for - more details. - - The results of the run could be accessed through the - :code:`allure_results` attribute. - """ - - examples_table = RstExampleTable.find_examples(self.request) - self.run_behave( - features=self.__resolve_code_blocks( - examples_table, - features, - feature_literals - ), - steps=self.__resolve_code_blocks( - examples_table, - steps, - step_literals - ), - environment=examples_table[environment] if environment else\ - environment_literal, - cli_args=cli_args - ) - - @staticmethod - def __extend_content_seq(testdir, content_seq, paths): - if content_seq is None: - content_seq = [] - if paths is None: - paths = [] - return chain( - content_seq, - (AllureBehaveRunner.__read_test_file(testdir, p) for p in paths) - ) - - @staticmethod - def __read_test_file(testdir, path): - fullpath = testdir.joinpath(path) - with open(fullpath, encoding="utf-8") as f: - return f.read() - - @staticmethod - def __resolve_content(testdir, content, path): - if content is None and path is not None: - content = AllureBehaveRunner.__read_test_file(testdir, path) - return content - - @staticmethod - def __pick_examples(table, example_names): return ( - table[n] for n in example_names - ) - - @staticmethod - def __resolve_code_blocks(table, code_block_names, literals): - code_block_names = code_block_names or [] - literals = literals or [] - return chain( - AllureBehaveRunner.__pick_examples(table, code_block_names), - literals - ) +from pytest import fixture +from .behave_runner import AllureBehaveRunner @fixture -def behave_runner(pytester: Pytester, request: FixtureRequest): - return AllureBehaveRunner(pytester, request) - - -__fix_behave_in_memory_run() +def behave_runner(request, pytester): + return AllureBehaveRunner(request, pytester) diff --git a/tests/allure_behave/defects/issue717_test.py b/tests/allure_behave/defects/issue717_test.py index 4e928e68..cfd5f371 100644 --- a/tests/allure_behave/defects/issue717_test.py +++ b/tests/allure_behave/defects/issue717_test.py @@ -5,9 +5,11 @@ from allure_commons_test.content import csv_equivalent from hamcrest import assert_that + class RowStub: def __init__(self, cells): self.cells = cells + def __iter__(self): return iter(self.cells) diff --git a/allure-nose2/test/__init__.py b/tests/allure_nose2/__init__.py similarity index 100% rename from allure-nose2/test/__init__.py rename to tests/allure_nose2/__init__.py diff --git a/allure-nose2/test/labels/__init__.py b/tests/allure_nose2/acceptance/__init__.py similarity index 100% rename from allure-nose2/test/labels/__init__.py rename to tests/allure_nose2/acceptance/__init__.py diff --git a/allure-nose2/test/parametrized/__init__.py b/tests/allure_nose2/acceptance/allure_api/__init__.py similarity index 100% rename from allure-nose2/test/parametrized/__init__.py rename to tests/allure_nose2/acceptance/allure_api/__init__.py diff --git a/allure-nose2/test/result/__init__.py b/tests/allure_nose2/acceptance/allure_api/labels/__init__.py similarity index 100% rename from allure-nose2/test/result/__init__.py rename to tests/allure_nose2/acceptance/allure_api/labels/__init__.py diff --git a/tests/allure_nose2/acceptance/allure_api/labels/test_bdd_labels.py b/tests/allure_nose2/acceptance/allure_api/labels/test_bdd_labels.py new file mode 100644 index 00000000..40576aec --- /dev/null +++ b/tests/allure_nose2/acceptance/allure_api/labels/test_bdd_labels.py @@ -0,0 +1,102 @@ +from hamcrest import assert_that +from tests.allure_nose2.nose2_runner import AllureNose2Runner + +from allure_commons_test.report import has_test_case +from allure_commons_test.label import has_epic +from allure_commons_test.label import has_feature + + +def test_method_label(nose2_runner: AllureNose2Runner): + """ + >>> import unittest + >>> import allure + + >>> class TestBDDLabelExample(unittest.TestCase): + ... @allure.epic("Label", "Bdd") + ... @allure.feature("Method label") + ... def test_method_label_example(self): + ... pass + """ + + allure_report = nose2_runner.run_docstring() + + assert_that( + allure_report, + has_test_case( + "test_method_label_example", + has_epic("Label"), + has_epic("Bdd"), + has_feature("Method label") + ) + ) + + +def test_class_label(nose2_runner: AllureNose2Runner): + """ + >>> import unittest + >>> import allure + + >>> @allure.epic("Label", "Bdd") + ... class TestBDDLabelExample(unittest.TestCase): + ... def test_class_label_example(self): + ... pass + """ + + allure_report = nose2_runner.run_docstring() + + assert_that( + allure_report, + has_test_case( + "test_class_label_example", + has_epic("Label"), + has_epic("Bdd"), + ) + ) + + +def test_class_method_label(nose2_runner: AllureNose2Runner): + """ + >>> import unittest + >>> import allure + + >>> @allure.epic("Label", "Bdd") + ... class TestBDDLabelExample(unittest.TestCase): + ... @allure.feature("Method label") + ... def test_class_and_method_label_example(self): + ... pass + """ + + allure_report = nose2_runner.run_docstring() + + assert_that( + allure_report, + has_test_case( + "test_class_and_method_label_example", + has_epic("Label"), + has_epic("Bdd"), + has_feature("Method label") + ) + ) + + +def test_func_label(nose2_runner: AllureNose2Runner): + """ + >>> import allure + + >>> @allure.epic("Label", "Bdd") + ... @allure.feature("Function label") + ... def test_func_label_example(): + ... pass + """ + + allure_report = nose2_runner.run_docstring() + + assert_that( + allure_report, + has_test_case( + "test_func_label_example", + has_epic("Label"), + has_epic("Bdd"), + has_feature("Function label") + ) + ) diff --git a/allure-nose2/test/with_mp/__init__.py b/tests/allure_nose2/acceptance/nose2_support/__init__.py similarity index 100% rename from allure-nose2/test/with_mp/__init__.py rename to tests/allure_nose2/acceptance/nose2_support/__init__.py diff --git a/allure-pytest-bdd/examples/scenario-outline/__init__.py b/tests/allure_nose2/acceptance/nose2_support/parametrized/__init__.py similarity index 100% rename from allure-pytest-bdd/examples/scenario-outline/__init__.py rename to tests/allure_nose2/acceptance/nose2_support/parametrized/__init__.py diff --git a/tests/allure_nose2/acceptance/nose2_support/parametrized/test_parametrized.py b/tests/allure_nose2/acceptance/nose2_support/parametrized/test_parametrized.py new file mode 100644 index 00000000..556bc52e --- /dev/null +++ b/tests/allure_nose2/acceptance/nose2_support/parametrized/test_parametrized.py @@ -0,0 +1,58 @@ +from hamcrest import assert_that, all_of +from tests.allure_nose2.nose2_runner import AllureNose2Runner + +from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_parameter + + +def test_parametrized_func(nose2_runner: AllureNose2Runner): + """ + >>> from nose2.tools import params + + >>> @params(("hello", 42), ("world", 777)) + ... def test_parametrized_func_example(alpha, betta): + ... pass + """ + + allure_report = nose2_runner.run_docstring() + + assert_that( + allure_report, + all_of( + has_test_case( + "test_parametrized_func_example", + has_parameter("alpha", "'hello'"), + has_parameter("betta", "42") + ), + has_test_case( + "test_parametrized_func_example", + has_parameter("alpha", "'world'"), + has_parameter("betta", "777") + ) + ) + ) + + +def test_parametrized_method(nose2_runner: AllureNose2Runner): + """ + >>> import unittest + >>> from nose2.tools import params + + >>> class TestParametrizedExample(unittest.TestCase): + ... @params(({"hello": 4}, [4, 2]), ({"wold": 2}, [7, 7, 7])) + ... def test_parametrized_method_example(self, bravo, charlie): + ... pass + """ + + allure_report = nose2_runner.run_docstring() + + assert_that( + allure_report, + all_of( + has_test_case( + "test_parametrized_method_example", + has_parameter("bravo", "{'hello': 4}"), + has_parameter("charlie", "[4, 2]") + ) + ) + ) diff --git a/allure-pytest-bdd/examples/simple-scenario/__init__.py b/tests/allure_nose2/acceptance/nose2_support/result/__init__.py similarity index 100% rename from allure-pytest-bdd/examples/simple-scenario/__init__.py rename to tests/allure_nose2/acceptance/nose2_support/result/__init__.py diff --git a/tests/allure_nose2/acceptance/nose2_support/result/test_fullname.py b/tests/allure_nose2/acceptance/nose2_support/result/test_fullname.py new file mode 100644 index 00000000..83fae6a9 --- /dev/null +++ b/tests/allure_nose2/acceptance/nose2_support/result/test_fullname.py @@ -0,0 +1,50 @@ +from hamcrest import assert_that +from tests.allure_nose2.nose2_runner import AllureNose2Runner +from hamcrest import has_entry, has_item, has_property + + +def test_func_fullname(nose2_runner: AllureNose2Runner): + """ + >>> def test_func_fullname_example(): + ... pass + """ + + allure_report = nose2_runner.run_docstring() + + assert_that( + allure_report, + has_property( + "test_cases", + has_item( + has_entry( + "fullName", + "test_func_fullname.test_func_fullname_example" + ) + ) + ) + ) + + +def test_method_fullname(nose2_runner: AllureNose2Runner): + """ + >>> import unittest + + >>> class TestFullnameExample(unittest.TestCase): + ... def test_method_fullname_example(self): + ... pass + """ + + allure_report = nose2_runner.run_docstring() + + assert_that( + allure_report, + has_property( + "test_cases", + has_item( + has_entry( + "fullName", + "test_method_fullname.TestFullnameExample.test_method_fullname_example" + ) + ) + ) + ) diff --git a/tests/allure_nose2/acceptance/nose2_support/result/test_status.py b/tests/allure_nose2/acceptance/nose2_support/result/test_status.py new file mode 100644 index 00000000..62c4b6e3 --- /dev/null +++ b/tests/allure_nose2/acceptance/nose2_support/result/test_status.py @@ -0,0 +1,96 @@ +from hamcrest import assert_that +from tests.allure_nose2.nose2_runner import AllureNose2Runner + +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status +from allure_commons_test.result import has_status_details +from allure_commons_test.result import with_message_contains + + +def test_passed_status(nose2_runner: AllureNose2Runner): + """ + >>> import unittest + + >>> class TestStatusExample(unittest.TestCase): + ... def test_passed_example(self): + ... assert True + """ + + allure_report = nose2_runner.run_docstring() + + assert_that( + allure_report, + has_test_case( + "test_passed_example", + with_status("passed") + ) + ) + + +def test_failed_status(nose2_runner: AllureNose2Runner): + """ + >>> import unittest + + >>> class TestStatusExample(unittest.TestCase): + ... def test_failed_example(self): + ... assert False, "my message" + """ + + allure_report = nose2_runner.run_docstring() + + assert_that( + allure_report, + has_test_case( + "test_failed_example", + with_status("failed"), + has_status_details( + with_message_contains("my message") + ) + ) + ) + + +def test_broken_status(nose2_runner: AllureNose2Runner): + """ + >>> import unittest + + >>> class TestStatusExample(unittest.TestCase): + ... def test_broken_example(self): + ... raise Exception("my error") + """ + + allure_report = nose2_runner.run_docstring() + + assert_that( + allure_report, + has_test_case( + "test_broken_example", + with_status("broken"), + has_status_details( + with_message_contains("my error") + ) + ) + ) + + +def test_skipped_status(nose2_runner: AllureNose2Runner): + """ + >>> import unittest + + >>> class TestStatusExample(unittest.TestCase): + ... def test_skipped_example(self): + ... self.skipTest('my skip reason') + """ + + allure_report = nose2_runner.run_docstring() + + assert_that( + allure_report, + has_test_case( + "test_skipped_example", + with_status("skipped"), + has_status_details( + with_message_contains("my skip reason") + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/fixture/function_scope/__init__.py b/tests/allure_nose2/acceptance/nose2_support/with_mp/__init__.py similarity index 100% rename from tests/allure_pytest/acceptance/fixture/function_scope/__init__.py rename to tests/allure_nose2/acceptance/nose2_support/with_mp/__init__.py diff --git a/allure-nose2/test/with_mp/test_pm.py b/tests/allure_nose2/acceptance/nose2_support/with_mp/test_mp.py similarity index 58% rename from allure-nose2/test/with_mp/test_pm.py rename to tests/allure_nose2/acceptance/nose2_support/with_mp/test_mp.py index 794f85e0..501b5c06 100644 --- a/allure-nose2/test/with_mp/test_pm.py +++ b/tests/allure_nose2/acceptance/nose2_support/with_mp/test_mp.py @@ -1,8 +1,8 @@ # Todo test mp -from test.example_runner import run_docstring_example +from tests.allure_nose2.nose2_runner import AllureNose2Runner -def test_func_fullname(): +def test_func_fullname(nose2_runner: AllureNose2Runner): """ >>> def test_func_fullname_example1(): ... pass @@ -11,4 +11,4 @@ def test_func_fullname(): >>> def test_func_fullname_example3(): ... pass """ - run_docstring_example() + nose2_runner.run_docstring() diff --git a/tests/allure_nose2/conftest.py b/tests/allure_nose2/conftest.py new file mode 100644 index 00000000..ce355c89 --- /dev/null +++ b/tests/allure_nose2/conftest.py @@ -0,0 +1,7 @@ +from .nose2_runner import AllureNose2Runner +from pytest import fixture + + +@fixture +def nose2_runner(request, pytester): + yield AllureNose2Runner(request, pytester) diff --git a/tests/allure_nose2/nose2_runner.py b/tests/allure_nose2/nose2_runner.py new file mode 100644 index 00000000..5829e442 --- /dev/null +++ b/tests/allure_nose2/nose2_runner.py @@ -0,0 +1,39 @@ +import importlib.machinery +import importlib.util +from doctest import script_from_examples +from nose2 import main +from pytest import FixtureRequest, Pytester +from tests.e2e import AllureFrameworkRunner + + +class AllureNose2Runner(AllureFrameworkRunner): + LOGGER_PATH = "allure_nose2.plugin.AllureFileLogger" + + def __init__(self, request: FixtureRequest, pytester: Pytester): + super().__init__(request, pytester) + + def run_docstring(self): + docstring = self._find_docstring() + example_code = script_from_examples(docstring) + spec = importlib.machinery.ModuleSpec(self.request.node.name, None) + module = importlib.util.module_from_spec(spec) + return self._run( + module, + example_code, + logger_path=AllureNose2Runner.LOGGER_PATH + ) + + def _run_framework(self, module, example): + # We execute the example here because the _run_framework runs in a + # nested allure context. Otherwise, all allure decorators used in the + # example would affect allure results of the testing system itself. + exec(example, module.__dict__) + main( + module=module, + argv=["nose2", "--allure"], + plugins=["allure_nose2.plugin"], + exit=False + ) + + +__all__ = ["AllureNose2Runner"] diff --git a/tests/allure_pytest/acceptance/attachment/attachment_class_test.py b/tests/allure_pytest/acceptance/attachment/attachment_class_test.py index a5c70521..0137e898 100644 --- a/tests/allure_pytest/acceptance/attachment/attachment_class_test.py +++ b/tests/allure_pytest/acceptance/attachment/attachment_class_test.py @@ -1,9 +1,10 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner from allure_commons_test.report import has_test_case from allure_commons_test.result import has_attachment -def test_class_method_attachment(executed_docstring_source, request): +def test_class_method_attachment(allure_pytest_runner: AllurePytestRunner): """ >>> import allure @@ -12,8 +13,12 @@ def test_class_method_attachment(executed_docstring_source, request): ... allure.attach("text", "failed", allure.attachment_type.TEXT) """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_class_method_attachment", - has_attachment(name="failed") - ) - ) + allure_report = allure_pytest_runner.run_docstring() + + assert_that( + allure_report, + has_test_case( + "test_class_method_attachment", + has_attachment(name="failed") + ) + ) diff --git a/tests/allure_pytest/acceptance/attachment/attachment_fixture_test.py b/tests/allure_pytest/acceptance/attachment/attachment_fixture_test.py index 734b1b5f..c14f18eb 100644 --- a/tests/allure_pytest/acceptance/attachment/attachment_fixture_test.py +++ b/tests/allure_pytest/acceptance/attachment/attachment_fixture_test.py @@ -1,6 +1,7 @@ """ ./allure-pytest/examples/attachment/attachment_fixture.rst """ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner from allure_commons_test.report import has_test_case from allure_commons_test.result import has_attachment from allure_commons_test.container import has_container @@ -8,25 +9,37 @@ from allure_commons_test.container import has_after -def test_fixture_attachment(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_fixture_attachment", - has_container(executed_docstring_path.allure_report, - has_before("fixture_with_attachment", - has_attachment() - ) - ) - ) +def test_fixture_attachment(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_fixture_attachment", + has_container( + allure_results, + has_before( + "fixture_with_attachment", + has_attachment() ) + ) + ) + ) + +def test_fixture_finalizer_attachment(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) -def test_fixture_finalizer_attachment(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_fixture_finalizer_attachment", - has_container(executed_docstring_path.allure_report, - has_after("fixture_with_attachment_in_finalizer::finalizer", - has_attachment() - ) - ) - ) + assert_that( + allure_results, + has_test_case( + "test_fixture_finalizer_attachment", + has_container( + allure_results, + has_after( + "fixture_with_attachment_in_finalizer::finalizer", + has_attachment() ) + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/attachment/attachment_hook_test.py b/tests/allure_pytest/acceptance/attachment/attachment_hook_test.py index d6238575..40999e62 100644 --- a/tests/allure_pytest/acceptance/attachment/attachment_hook_test.py +++ b/tests/allure_pytest/acceptance/attachment/attachment_hook_test.py @@ -1,49 +1,57 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_attachment -def test_attach_from_runtest_teardown(allured_testdir): - allured_testdir.testdir.makeconftest(""" - import allure - - - def pytest_runtest_teardown(*args, **kwargs): - allure.attach(body="body", name="attachment from teardown") - """) - - allured_testdir.testdir.makepyfile(""" +def test_attach_from_runtest_teardown(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_pytest( + """ def test_attach_from_runtest_teardown(): pass - """) - - allured_testdir.run_with_allure() - - assert_that(allured_testdir.allure_report, - has_test_case("test_attach_from_runtest_teardown", - has_attachment(name="attachment from teardown"), - ) - ) - - -def test_attach_from_runtest_logfinish(allured_testdir): - allured_testdir.testdir.makeconftest(""" - import allure - - - def pytest_runtest_logfinish(*args, **kwargs): - allure.attach(body="body", name="attachment from logfinish") - """) - - allured_testdir.testdir.makepyfile(""" + """, + conftest_literal=( + """ + import allure + + + def pytest_runtest_teardown(*args, **kwargs): + allure.attach(body="body", name="attachment from teardown") + """ + ) + ) + + assert_that( + allure_results, + has_test_case( + "test_attach_from_runtest_teardown", + has_attachment(name="attachment from teardown") + ) + ) + + +def test_attach_from_runtest_logfinish(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_pytest( + """ def test_attach_from_runtest_logfinish(): pass - """) - - allured_testdir.run_with_allure() - - assert_that(allured_testdir.allure_report, - has_test_case("test_attach_from_runtest_logfinish", - has_attachment(name="attachment from logfinish"), - ) - ) + """, + conftest_literal=( + """ + import allure + + + def pytest_runtest_logfinish(*args, **kwargs): + allure.attach(body="body", name="attachment from logfinish") + """ + ) + ) + + assert_that( + allure_results, + has_test_case( + "test_attach_from_runtest_logfinish", + has_attachment(name="attachment from logfinish") + ) + ) diff --git a/tests/allure_pytest/acceptance/attachment/attachment_parametrized_test.py b/tests/allure_pytest/acceptance/attachment/attachment_parametrized_test.py index 05a8d4ad..0016e2df 100644 --- a/tests/allure_pytest/acceptance/attachment/attachment_parametrized_test.py +++ b/tests/allure_pytest/acceptance/attachment/attachment_parametrized_test.py @@ -1,13 +1,13 @@ -import pytest from hamcrest import assert_that from hamcrest import all_of -from hamcrest import has_property, has_value -from hamcrest import contains_string +from hamcrest import equal_to +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_attachment_with_content -@pytest.mark.parametrize("param", ["first", "second"]) -def test_parametrized_attachment(executed_docstring_source, param): +def test_parametrized_attachment(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest >>> import allure @@ -17,10 +17,24 @@ def test_parametrized_attachment(executed_docstring_source, param): ... allure.attach(param) """ - assert_that(executed_docstring_source.allure_report, - all_of( - has_test_case(f"test_parametrized_attachment_example[{param}]"), - has_property("attachments", - has_value(contains_string(param)) - ) - )) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + all_of( + has_test_case( + "test_parametrized_attachment_example[first]", + has_attachment_with_content( + allure_results.attachments, + content_matcher=equal_to("first") + ) + ), + has_test_case( + "test_parametrized_attachment_example[second]", + has_attachment_with_content( + allure_results.attachments, + content_matcher=equal_to("second") + ) + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/attachment/attachment_step_test.py b/tests/allure_pytest/acceptance/attachment/attachment_step_test.py index da544978..0ffa7d6f 100644 --- a/tests/allure_pytest/acceptance/attachment/attachment_step_test.py +++ b/tests/allure_pytest/acceptance/attachment/attachment_step_test.py @@ -1,48 +1,64 @@ """ ./allure-pytest/examples/attachment/attachment_step.rst """ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_step from allure_commons_test.result import has_attachment -def test_step_with_attachment(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_step_with_attachment", - has_step("step_with_attachment", - has_attachment() - ), - ) - ) +def test_step_with_attachment(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples() + + assert_that( + allure_results, + has_test_case( + "test_step_with_attachment", + has_step( + "step_with_attachment", + has_attachment() + ), + ) + ) -def test_step_with_thread_and_attachment(allured_testdir): - allured_testdir.testdir.makepyfile( +def test_step_with_thread_and_attachment(allure_pytest_runner: AllurePytestRunner): + testfile_content = ( """ - from concurrent.futures import ThreadPoolExecutor + from concurrent.futures import ThreadPoolExecutor - import allure - import pytest + import allure + import pytest - @allure.step("thread {x}") - def parallel_step(x=1): - allure.attach("text", str(x), allure.attachment_type.TEXT) + @allure.step("thread {x}") + def parallel_step(x=1): + allure.attach("text", str(x), allure.attachment_type.TEXT) - def test_thread(): - with allure.step("Start in thread"): - with ThreadPoolExecutor(max_workers=2) as executor: - f_result = executor.map(parallel_step, [1, 2]) - """ + def test_thread(): + with allure.step("Start in thread"): + with ThreadPoolExecutor(max_workers=2) as executor: + f_result = executor.map(parallel_step, [1, 2]) + """ ) - allured_testdir.run_with_allure() + allure_results = allure_pytest_runner.run_pytest(testfile_content) - assert_that(allured_testdir.allure_report, - has_test_case("test_thread", - has_step("Start in thread", - has_step("thread 1", has_attachment(name="1")), - has_step("thread 2", has_attachment(name="2")), - ) - ) + assert_that( + allure_results, + has_test_case( + "test_thread", + has_step( + "Start in thread", + has_step( + "thread 1", + has_attachment(name="1") + ), + has_step( + "thread 2", + has_attachment(name="2") ) + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/attachment/attachment_test.py b/tests/allure_pytest/acceptance/attachment/attachment_test.py index 8fd3c596..3c2015ba 100644 --- a/tests/allure_pytest/acceptance/attachment/attachment_test.py +++ b/tests/allure_pytest/acceptance/attachment/attachment_test.py @@ -1,29 +1,46 @@ """ ./allure-pytest/examples/attachment/attachment.rst """ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_attachment -def test_attach_body_with_default_kwargs(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_attach_body_with_default_kwargs", - has_attachment() - ) - ) +def test_attach_body_with_default_kwargs(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_attach_body_with_default_kwargs", + has_attachment() + ) + ) + + +def test_attach_body(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + assert_that( + allure_results, + has_test_case( + "test_attach_body", + has_attachment( + attach_type="application/xml", + name="some attachment name" + ) + ) + ) -def test_attach_body(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_attach_body", - has_attachment(attach_type="application/xml", name="some attachment name") - ) - ) +def test_attach_file(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) -def test_attach_file(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_attach_file", - has_attachment() - ) - ) + assert_that( + allure_results, + has_test_case( + "test_attach_file", + has_attachment() + ) + ) diff --git a/tests/allure_pytest/acceptance/capture/capture_attach_test.py b/tests/allure_pytest/acceptance/capture/capture_attach_test.py index b438eb79..64f538f4 100644 --- a/tests/allure_pytest/acceptance/capture/capture_attach_test.py +++ b/tests/allure_pytest/acceptance/capture/capture_attach_test.py @@ -3,10 +3,11 @@ from hamcrest import all_of, is_, is_not, empty from hamcrest import has_property, has_value from hamcrest import contains_string +from tests.allure_pytest.pytest_runner import AllurePytestRunner @pytest.mark.parametrize("capture", ["sys", "fd", "no"]) -def test_capture_stdout(allured_testdir, capture): +def test_capture_stdout(allure_pytest_runner: AllurePytestRunner, capture): """ >>> import pytest >>> import allure @@ -24,25 +25,26 @@ def test_capture_stdout(allured_testdir, capture): ... print ("Start step") """ - allured_testdir.parse_docstring_source() - allured_testdir.run_with_allure(f"--capture={capture}") + allure_results = allure_pytest_runner.run_docstring(f"--capture={capture}") if_pytest_capture_ = is_not if capture == "no" else is_ - assert_that(allured_testdir.allure_report, - has_property("attachments", - all_of( - if_pytest_capture_(has_value(contains_string("Start fixture"))), - if_pytest_capture_(has_value(contains_string("Stop fixture"))), - if_pytest_capture_(has_value(contains_string("Start test"))), - if_pytest_capture_(has_value(contains_string("Start step"))) - ) - ) - ) + assert_that( + allure_results, + has_property( + "attachments", + all_of( + if_pytest_capture_(has_value(contains_string("Start fixture"))), + if_pytest_capture_(has_value(contains_string("Stop fixture"))), + if_pytest_capture_(has_value(contains_string("Start test"))), + if_pytest_capture_(has_value(contains_string("Start step"))) + ) + ) + ) @pytest.mark.parametrize("capture", ["sys", "fd"]) -def test_capture_empty_stdout(allured_testdir, capture): +def test_capture_empty_stdout(allure_pytest_runner: AllurePytestRunner, capture): """ >>> import pytest >>> import allure @@ -58,16 +60,16 @@ def test_capture_empty_stdout(allured_testdir, capture): ... pass """ - allured_testdir.parse_docstring_source() - allured_testdir.run_with_allure(f"--capture={capture}") + allure_results = allure_pytest_runner.run_docstring(f"--capture={capture}") - assert_that(allured_testdir.allure_report, - has_property("attachments", empty()) - ) + assert_that( + allure_results, + has_property("attachments", empty()) + ) @pytest.mark.parametrize("logging", [True, False]) -def test_capture_log(allured_testdir, logging): +def test_capture_log(allure_pytest_runner: AllurePytestRunner, logging): """ >>> import logging >>> import pytest @@ -88,39 +90,45 @@ def test_capture_log(allured_testdir, logging): ... logger.info("Start step") """ - allured_testdir.parse_docstring_source() - params = [] if logging else ["-p", "no:logging"] - if_logging_ = is_ if logging else is_not - - allured_testdir.run_with_allure("--log-cli-level=INFO", *params) - - assert_that(allured_testdir.allure_report, - has_property("attachments", - all_of( - if_logging_(has_value(contains_string("Start fixture"))), - if_logging_(has_value(contains_string("Stop fixture"))), - if_logging_(has_value(contains_string("Start test"))), - if_logging_(has_value(contains_string("Start step"))) - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring( + "--log-level=INFO", + *params + ) + if_logging_ = is_ if logging else is_not -def test_capture_disabled(allured_testdir): + assert_that( + allure_results, + has_property( + "attachments", + all_of( + if_logging_(has_value(contains_string("Start fixture"))), + if_logging_(has_value(contains_string("Stop fixture"))), + if_logging_(has_value(contains_string("Start test"))), + if_logging_(has_value(contains_string("Start step"))) + ) + ) + ) + + +def test_capture_disabled(allure_pytest_runner: AllurePytestRunner): """ >>> import logging >>> logger = logging.getLogger(__name__) >>> def test_capture_disabled_example(): ... logger.info("Start logging") - ... print ("Start printing") + ... #print ("Start printing") """ - allured_testdir.parse_docstring_source() - allured_testdir.run_with_allure("--log-cli-level=INFO", "--allure-no-capture") + allure_results = allure_pytest_runner.run_docstring( + "--log-level=INFO", + "--allure-no-capture" + ) - assert_that(allured_testdir.allure_report, - has_property("attachments", empty()) - ) + assert_that( + allure_results, + has_property("attachments", empty()) + ) diff --git a/tests/allure_pytest/acceptance/description/description_test.py b/tests/allure_pytest/acceptance/description/description_test.py index dd28fc1e..195538a9 100644 --- a/tests/allure_pytest/acceptance/description/description_test.py +++ b/tests/allure_pytest/acceptance/description/description_test.py @@ -1,37 +1,49 @@ """ ./allure-pytest/examples/description/description.rst """ from hamcrest import assert_that, contains_string +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_description, has_description_html -def test_description(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_description", - has_description(contains_string("Test description")) - ) - ) - - -def test_description_html(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_description_html", - has_description_html(contains_string("

Html test description

")) - ) - ) - - -def test_docstring_description(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_docstring_description", - has_description(contains_string("Docstring")) - ) - ) - - -def test_unicode_docstring_description(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_unicode_docstring_description", - has_description(contains_string("Докстринг в юникоде")) - ) - ) +def test_description(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_description", + has_description( + contains_string("Test description") + ) + ) + ) + + +def test_description_html(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_description_html", + has_description_html( + contains_string("

Html test description

") + ) + ) + ) + + +def test_docstring_description(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_docstring_description", + has_description( + contains_string("Docstring") + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/description/dynamic_description_test.py b/tests/allure_pytest/acceptance/description/dynamic_description_test.py index f98fe5f9..5af57321 100644 --- a/tests/allure_pytest/acceptance/description/dynamic_description_test.py +++ b/tests/allure_pytest/acceptance/description/dynamic_description_test.py @@ -1,21 +1,35 @@ """./allure-pytest/examples/description/dynamic_description.rst""" from hamcrest import assert_that, contains_string +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_description, has_description_html -def test_dynamic_description(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_dynamic_description", - has_description(contains_string("Actual description")) - ) - ) +def test_dynamic_description(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_dynamic_description", + has_description( + contains_string("Actual description") + ) + ) + ) + +def test_dynamic_description_html(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) -def test_dynamic_description_html(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_dynamic_description_html", - has_description_html(contains_string("

Actual HTML description

")) - ) - ) + assert_that( + allure_results, + has_test_case( + "test_dynamic_description_html", + has_description_html( + contains_string("

Actual HTML description

") + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/display_name/display_name_test.py b/tests/allure_pytest/acceptance/display_name/display_name_test.py index d017cc6d..a0985b1d 100644 --- a/tests/allure_pytest/acceptance/display_name/display_name_test.py +++ b/tests/allure_pytest/acceptance/display_name/display_name_test.py @@ -1,60 +1,38 @@ """ ./allure-pytest/examples/display_name/display_name.rst""" from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_title from allure_commons_test.label import has_label -def test_display_name(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_display_name", - has_title("A some test title") - ) - ) - - -def test_display_name_template(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_display_name_template", - has_title("A some test title with param False") - ) - ) - - -def test_unicode_display_name(executed_docstring_source): - """ - >>> import allure - - >>> @allure.title("Лунтик") - >>> def test_unicode_display_name_example(): - ... pass - """ - - assert_that(executed_docstring_source.allure_report, - has_test_case("test_unicode_display_name_example", has_title("Лунтик")) - ) +def test_display_name(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + assert_that( + allure_results, + has_test_case( + "test_display_name", + has_title("A some test title") + ) + ) -def test_unicode_display_name_template(executed_docstring_source): - """ - >>> import allure - >>> import pytest - >>> @allure.title("Тест с шаблоном и параметром: {param}") - ... @pytest.mark.parametrize("param", [False]) - ... def test_unicode_display_name_template_example(param): - ... assert param - """ +def test_display_name_template(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) - assert_that(executed_docstring_source.allure_report, - has_test_case("test_unicode_display_name_template_example", - has_title("Тест с шаблоном и параметром: False") - ) - ) + assert_that( + allure_results, + has_test_case( + "test_display_name_template", + has_title("A some test title with param False") + ) + ) -def test_fixture_value_in_display_name(executed_docstring_source): +def test_fixture_value_in_display_name(allure_pytest_runner: AllurePytestRunner): """ >>> import allure >>> import pytest @@ -68,37 +46,43 @@ def test_fixture_value_in_display_name(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_fixture_value_name", - has_title("title with fixture value") - ) - ) + allure_results = allure_pytest_runner.run_docstring() + assert_that( + allure_results, + has_test_case( + "test_fixture_value_name", + has_title("title with fixture value") + ) + ) -def test_display_name_with_features(allured_testdir): - allured_testdir.testdir.makepyfile(""" - import allure - import pytest - @allure.feature('Feature 1') - @allure.title('Titled test with features') - @allure.feature('Feature 2') - def test_feature_label_for_titled_test(): - pass - """) +def test_display_name_with_features(allure_pytest_runner: AllurePytestRunner): + """ + >>> import allure + >>> import pytest - allured_testdir.run_with_allure() + >>> @allure.feature('Feature 1') + ... @allure.title('Titled test with features') + ... @allure.feature('Feature 2') + ... def test_feature_label_for_titled_test(): + ... pass + """ - assert_that(allured_testdir.allure_report, - has_test_case("test_feature_label_for_titled_test", - has_label("feature", "Feature 1"), - has_label("feature", "Feature 2"), - has_title("Titled test with features") - ) - ) + allure_results = allure_pytest_runner.run_docstring() + assert_that( + allure_results, + has_test_case( + "test_feature_label_for_titled_test", + has_label("feature", "Feature 1"), + has_label("feature", "Feature 2"), + has_title("Titled test with features") + ) + ) -def test_failed_fixture_value_in_display_name(executed_docstring_source): + +def test_failed_fixture_value_in_display_name(allure_pytest_runner: AllurePytestRunner): """ >>> import allure >>> import pytest @@ -112,8 +96,12 @@ def test_failed_fixture_value_in_display_name(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_fixture_value_name", - has_title("title with {fix}") - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_fixture_value_name", + has_title("title with {fix}") + ) + ) diff --git a/tests/allure_pytest/acceptance/display_name/dynamic_display_name_test.py b/tests/allure_pytest/acceptance/display_name/dynamic_display_name_test.py index af243cb2..20289c88 100644 --- a/tests/allure_pytest/acceptance/display_name/dynamic_display_name_test.py +++ b/tests/allure_pytest/acceptance/display_name/dynamic_display_name_test.py @@ -1,13 +1,19 @@ """ ./allure-pytest/examples/display_name/dynamic_display_name.rst """ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_title -def test_dynamic_display_name(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_dynamic_display_name", - has_title("It is renamed test") - ) - ) +def test_dynamic_display_name(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples() + + assert_that( + allure_results, + has_test_case( + "test_dynamic_display_name", + has_title("It is renamed test") + ) + ) diff --git a/tests/allure_pytest/acceptance/duration/duration_time_test.py b/tests/allure_pytest/acceptance/duration/duration_time_test.py index 68fd51e1..9d201824 100644 --- a/tests/allure_pytest/acceptance/duration/duration_time_test.py +++ b/tests/allure_pytest/acceptance/duration/duration_time_test.py @@ -1,6 +1,8 @@ import allure import pytest -from hamcrest import assert_that, has_entry, greater_than, all_of +from hamcrest import assert_that, has_entry, greater_than, all_of, less_than +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons.utils import now @@ -18,22 +20,28 @@ @pytest.mark.parametrize("snippet", snippets) -def test_duration(allured_testdir, snippet): - allured_testdir.testdir.makepyfile(f""" +def test_duration(allure_pytest_runner: AllurePytestRunner, snippet): + testfile_content = ( + f""" def test_duration_example(): {snippet} - """) + """ + ) - timestamp = now() - allured_testdir.run_with_allure() + before = now() + allure_results = allure_pytest_runner.run_pytest(testfile_content) + after = now() assert_that( - allured_testdir.allure_report, + allure_results, has_test_case( "test_duration_example", all_of( - has_entry("start", greater_than(timestamp)), - has_entry("stop", greater_than(timestamp)) + has_entry("start", greater_than(before)), + has_entry("stop", all_of( + greater_than(before), + less_than(after) + )) ) ) ) @@ -41,8 +49,9 @@ def test_duration_example(): @allure.issue("244") @pytest.mark.parametrize("snippet", snippets) -def test_with_fixture_duration(allured_testdir, snippet): - allured_testdir.testdir.makepyfile(f""" +def test_with_fixture_duration(allure_pytest_runner: AllurePytestRunner, snippet): + testfile_content = ( + f""" import pytest @pytest.fixture @@ -51,18 +60,23 @@ def fixture(): def test_with_fixture_duration_example(fixture): pass - """) + """ + ) - timestamp = now() - allured_testdir.run_with_allure() + before = now() + allure_results = allure_pytest_runner.run_pytest(testfile_content) + after = now() assert_that( - allured_testdir.allure_report, + allure_results, has_test_case( "test_with_fixture_duration_example", all_of( - has_entry("start", greater_than(timestamp)), - has_entry("stop", greater_than(timestamp)) + has_entry("start", greater_than(before)), + has_entry("stop", all_of( + greater_than(before), + less_than(after) + )) ) ) ) @@ -70,8 +84,12 @@ def test_with_fixture_duration_example(fixture): @allure.issue("244") @pytest.mark.parametrize("snippet", snippets) -def test_with_fixture_finalizer_duration(allured_testdir, snippet): - allured_testdir.testdir.makepyfile(f""" +def test_with_fixture_finalizer_duration( + allure_pytest_runner: AllurePytestRunner, + snippet +): + testfile_content = ( + f""" import pytest @pytest.fixture @@ -82,24 +100,33 @@ def finalizer(): def test_with_fixture_finalizer_duration(fixture): pass - """) + """ + ) - timestamp = now() - allured_testdir.run_with_allure() + before = now() + allure_results = allure_pytest_runner.run_pytest(testfile_content) + after = now() - assert_that(allured_testdir.allure_report, - has_test_case("test_with_fixture_finalizer_duration", - all_of( - has_entry("start", greater_than(timestamp)), - has_entry("stop", greater_than(timestamp)) - )) - ) + assert_that( + allure_results, + has_test_case( + "test_with_fixture_finalizer_duration", + all_of( + has_entry("start", greater_than(before)), + has_entry("stop", all_of( + greater_than(before), + less_than(after) + )) + ) + ) + ) -def test_test_skipped_if_fixture_exits(allured_testdir): +def test_test_skipped_if_fixture_exits(allure_pytest_runner: AllurePytestRunner): """Test should be market as skipped: pytest reports it as 'not run'""" - allured_testdir.testdir.makepyfile(f""" + testfile_content = ( + """ import pytest @pytest.fixture @@ -108,13 +135,13 @@ def fixture(): def test_with_fixture_duration_example(fixture): pass - """) + """ + ) - timestamp = now() - allured_testdir.run_with_allure() + allure_results = allure_pytest_runner.run_pytest(testfile_content) assert_that( - allured_testdir.allure_report, + allure_results, has_test_case( "test_with_fixture_duration_example", with_status("skipped") diff --git a/tests/allure_pytest/acceptance/fixture/fixture_finalized_test.py b/tests/allure_pytest/acceptance/fixture/fixture_finalized_test.py index a5cab436..2b1f9737 100644 --- a/tests/allure_pytest/acceptance/fixture/fixture_finalized_test.py +++ b/tests/allure_pytest/acceptance/fixture/fixture_finalized_test.py @@ -1,5 +1,7 @@ import allure from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.container import has_container from allure_commons_test.container import has_before, has_after @@ -7,7 +9,7 @@ @allure.feature("Fixture") @allure.story("Fixture finalizer") -def test_fixture_finalizer(executed_docstring_source): +def test_fixture_finalizer(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -23,19 +25,24 @@ def test_fixture_finalizer(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_fixture_with_finalizer_example", - has_container(executed_docstring_source.allure_report, - has_before("fixture_with_finalizer"), - has_after("fixture_with_finalizer::finalizer") - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_fixture_with_finalizer_example", + has_container( + allure_results, + has_before("fixture_with_finalizer"), + has_after("fixture_with_finalizer::finalizer") + ) + ) + ) @allure.feature("Fixture") @allure.story("Fixture finalizer") -def test_fixture_finalizers(executed_docstring_source): +def test_fixture_finalizers(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -55,12 +62,17 @@ def test_fixture_finalizers(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_fixture_with_finalizers_example", - has_container(executed_docstring_source.allure_report, - has_before("fixture_with_finalizers"), - has_after("fixture_with_finalizers::first_finalizer"), - has_after("fixture_with_finalizers::second_finalizer") - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_fixture_with_finalizers_example", + has_container( + allure_results, + has_before("fixture_with_finalizers"), + has_after("fixture_with_finalizers::first_finalizer"), + has_after("fixture_with_finalizers::second_finalizer") + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/fixture/fixture_test.py b/tests/allure_pytest/acceptance/fixture/fixture_test.py index 0167a308..68562282 100644 --- a/tests/allure_pytest/acceptance/fixture/fixture_test.py +++ b/tests/allure_pytest/acceptance/fixture/fixture_test.py @@ -1,10 +1,12 @@ import pytest +from hamcrest import assert_that, not_, all_of +from itertools import combinations_with_replacement +from tests.allure_pytest.pytest_runner import AllurePytestRunner + import allure -from hamcrest import assert_that, not_ from allure_commons_test.report import has_test_case from allure_commons_test.container import has_container, has_before, has_after from allure_commons_test.result import has_step -from itertools import combinations_with_replacement fixture_scopes = ["session", "module", "class", "function"] @@ -12,8 +14,9 @@ @allure.feature("Fixture") @pytest.mark.parametrize("first_scope", fixture_scopes) @pytest.mark.parametrize("second_scope", fixture_scopes) -def test_fixture(allured_testdir, first_scope, second_scope): - allured_testdir.testdir.makepyfile(f""" +def test_fixture(allure_pytest_runner: AllurePytestRunner, first_scope, second_scope): + testfile_content = ( + f""" import pytest @pytest.fixture(scope="{first_scope}") @@ -26,28 +29,28 @@ def second_fixture(): def test_fixture_example(first_fixture, second_fixture): pass - """) - - allured_testdir.run_with_allure() - - assert_that(allured_testdir.allure_report, - has_test_case("test_fixture_example", - has_container(allured_testdir.allure_report, - has_before("first_fixture") - ), - has_container(allured_testdir.allure_report, - has_before("second_fixture"), - ) - ) - ) + """ + ) + + allure_results = allure_pytest_runner.run_pytest(testfile_content) + + assert_that( + allure_results, + has_test_case( + "test_fixture_example", + has_container(allure_results, has_before("first_fixture")), + has_container(allure_results, has_before("second_fixture")) + ) + ) @pytest.mark.parametrize( ["parent_scope", "child_scope"], list(combinations_with_replacement(fixture_scopes, 2)) ) -def test_nested_fixture(allured_testdir, parent_scope, child_scope): - allured_testdir.testdir.makepyfile(f""" +def test_nested_fixture(allure_pytest_runner: AllurePytestRunner, parent_scope, child_scope): + testfile_content = ( + f""" import pytest @pytest.fixture(scope="{parent_scope}") @@ -63,37 +66,32 @@ def test_nested_fixture_example(child_fixture): def test_fixture_used_in_other_fixtures_example(parent_fixture): pass - - """) - - allured_testdir.run_with_allure() - - assert_that(allured_testdir.allure_report, - has_test_case("test_nested_fixture_example", - has_container(allured_testdir.allure_report, - has_before("parent_fixture") - ), - has_container(allured_testdir.allure_report, - has_before("child_fixture"), - ) - ) - ) - - assert_that(allured_testdir.allure_report, - has_test_case("test_fixture_used_in_other_fixtures_example", - has_container(allured_testdir.allure_report, - has_before("parent_fixture") - ), - not_(has_container(allured_testdir.allure_report, - has_before("child_fixture"), - ) - ) - ) - ) + """ + ) + + allure_results = allure_pytest_runner.run_pytest(testfile_content) + + assert_that( + allure_results, + has_test_case( + "test_nested_fixture_example", + has_container(allure_results, has_before("parent_fixture")), + has_container(allure_results, has_before("child_fixture")) + ) + ) + + assert_that( + allure_results, + has_test_case( + "test_fixture_used_in_other_fixtures_example", + has_container(allure_results, has_before("parent_fixture")), + not_(has_container(allure_results, has_before("child_fixture"))) + ) + ) @allure.feature("Fixture") -def test_nested_fixtures(executed_docstring_source): +def test_nested_fixtures(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -117,24 +115,23 @@ def test_nested_fixtures(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_nested_fixtures_example", - has_container(executed_docstring_source.allure_report, - has_before("first_fixture") - ), - has_container(executed_docstring_source.allure_report, - has_before("second_fixture"), - ), - has_container(executed_docstring_source.allure_report, - has_before("child_fixture"), - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_nested_fixtures_example", + has_container(allure_results, has_before("first_fixture")), + has_container(allure_results, has_before("second_fixture")), + has_container(allure_results, has_before("child_fixture")) + ) + ) @allure.feature("Fixture") -def test_fixture_allure_title(allured_testdir): - allured_testdir.testdir.makepyfile(""" +def test_fixture_allure_title(allure_pytest_runner: AllurePytestRunner): + testfile_content = ( + """ import pytest import allure @@ -145,22 +142,27 @@ def first_fixture(): def test_titled_fixture_example(first_fixture): pass - """) + """ + ) - allured_testdir.run_with_allure() + allure_results = allure_pytest_runner.run_pytest(testfile_content) - assert_that(allured_testdir.allure_report, - has_test_case("test_titled_fixture_example", - has_container(allured_testdir.allure_report, - has_before("Allure fixture title") - ) - ) - ) + assert_that( + allure_results, + has_test_case( + "test_titled_fixture_example", + has_container( + allure_results, + has_before("Allure fixture title") + ) + ) + ) @allure.feature("Fixture") -def test_fixture_allure_title_before(allured_testdir): - allured_testdir.testdir.makepyfile(""" +def test_fixture_allure_title_before(allure_pytest_runner: AllurePytestRunner): + testfile_content = ( + """ import pytest import allure @@ -171,21 +173,26 @@ def first_fixture(): def test_titled_before_fixture_example(first_fixture): pass - """) - - allured_testdir.run_with_allure() - - assert_that(allured_testdir.allure_report, - has_test_case("test_titled_before_fixture_example", - has_container(allured_testdir.allure_report, - has_before("Allure fixture title") - ) - ) - ) - - -def test_titled_fixture_from_conftest(allured_testdir): - allured_testdir.testdir.makeconftest(""" + """ + ) + + allure_results = allure_pytest_runner.run_pytest(testfile_content) + + assert_that( + allure_results, + has_test_case( + "test_titled_before_fixture_example", + has_container( + allure_results, + has_before("Allure fixture title") + ) + ) + ) + + +def test_titled_fixture_from_conftest(allure_pytest_runner: AllurePytestRunner): + conftest_content = ( + """ import allure import pytest @@ -198,29 +205,40 @@ def first_fixture(): @allure.title('Titled fixture after pytest.fixture') def second_fixture(): pass - """) + """ + ) - allured_testdir.testdir.makepyfile(""" + testfile_content = ( + """ def test_with_titled_conftest_fixtures(first_fixture, second_fixture): pass - """) - - allured_testdir.run_with_allure() - - assert_that(allured_testdir.allure_report, - has_test_case("test_with_titled_conftest_fixtures", - has_container(allured_testdir.allure_report, - has_before("Titled fixture before pytest.fixture") - ), - has_container(allured_testdir.allure_report, - has_before("Titled fixture after pytest.fixture") - ) - ) - ) - - -def test_fixture_override(allured_testdir): - allured_testdir.testdir.makeconftest(""" + """ + ) + + allure_results = allure_pytest_runner.run_pytest( + testfile_content, + conftest_literal=conftest_content + ) + + assert_that( + allure_results, + has_test_case( + "test_with_titled_conftest_fixtures", + has_container( + allure_results, + has_before("Titled fixture before pytest.fixture") + ), + has_container( + allure_results, + has_before("Titled fixture after pytest.fixture") + ) + ) + ) + + +def test_fixture_override(allure_pytest_runner: AllurePytestRunner): + conftest_content = ( + """ import pytest import allure @@ -232,9 +250,11 @@ def my_fixture(): with allure.step('Step in after in original fixture'): pass - """) + """ + ) - allured_testdir.testdir.makepyfile(""" + testfile_content = ( + """ import pytest import allure @@ -248,38 +268,55 @@ def my_fixture(my_fixture): def test_with_redefined_fixture(my_fixture): pass - """) - - allured_testdir.run_with_allure() - - assert_that(allured_testdir.allure_report, - has_test_case("test_with_redefined_fixture", - has_container(allured_testdir.allure_report, - has_before("my_fixture", - has_step("Step in before in original fixture") - ), - has_after("my_fixture::0", - has_step("Step in after in original fixture") - ) - ), - has_container(allured_testdir.allure_report, - has_before("my_fixture", - has_step("Step in before in redefined fixture") - ), - has_after("my_fixture::0", - has_step("Step in after in redefined fixture") - ) - ), - ) + """ + ) + + allure_results = allure_pytest_runner.run_pytest( + testfile_content, + conftest_literal=conftest_content + ) + + assert_that( + allure_results, + has_test_case( + "test_with_redefined_fixture", + has_container( + allure_results, + has_before( + "my_fixture", + has_step("Step in before in original fixture") + ), + has_after( + "my_fixture::0", + has_step("Step in after in original fixture") + ) + ), + has_container( + allure_results, + has_before( + "my_fixture", + has_step("Step in before in redefined fixture") + ), + has_after( + "my_fixture::0", + has_step("Step in after in redefined fixture") ) + ) + ) + ) @pytest.mark.parametrize( ["parent_scope", "child_scope"], list(combinations_with_replacement(fixture_scopes, 2)) ) -def test_dynamically_called_fixture(allured_testdir, parent_scope, child_scope): - allured_testdir.testdir.makepyfile(f""" +def test_dynamically_called_fixture( + allure_pytest_runner: AllurePytestRunner, + parent_scope, + child_scope +): + testfile_content = ( + f""" import pytest @pytest.fixture(scope="{parent_scope}", autouse=True) @@ -306,108 +343,155 @@ def test_two(request): def test_three(request): request.getfixturevalue('parent_dyn_call_fixture') - """) - - allured_testdir.run_with_allure() - - assert_that(allured_testdir.allure_report, - has_test_case("test_one", - has_container(allured_testdir.allure_report, - has_before("parent_auto_call_fixture"), - has_after("parent_auto_call_fixture::0"), - ), - has_container(allured_testdir.allure_report, - has_before("child_manual_call_fixture"), - has_after("child_manual_call_fixture::0"), - ), - not_(has_container(allured_testdir.allure_report, - has_before("parent_dyn_call_fixture"), - has_after("parent_dyn_call_fixture::0"), - ), - ), - not_(has_container(allured_testdir.allure_report, - has_before("child_dyn_call_fixture"), - ), - ) - ) + """ + ) + + allure_results = allure_pytest_runner.run_pytest(testfile_content) + + assert_that( + allure_results, + all_of( + has_test_case( + "test_one", + has_container( + allure_results, + has_before("parent_auto_call_fixture"), + has_after("parent_auto_call_fixture::0") + ), + has_container( + allure_results, + has_before("child_manual_call_fixture"), + has_after("child_manual_call_fixture::0") + ), + not_( + has_container( + allure_results, + has_before("parent_dyn_call_fixture"), + has_after("parent_dyn_call_fixture::0") + ), + ), + not_( + has_container( + allure_results, + has_before("child_dyn_call_fixture") + ), ) - assert_that(allured_testdir.allure_report, - has_test_case("test_two", - has_container(allured_testdir.allure_report, - has_before("parent_auto_call_fixture"), - has_after("parent_auto_call_fixture::0"), - ), - not_(has_container(allured_testdir.allure_report, - has_before("child_manual_call_fixture"), - has_after("child_manual_call_fixture::0"), - ), - ), - has_container(allured_testdir.allure_report, - has_before("parent_dyn_call_fixture"), - has_after("parent_dyn_call_fixture::0"), - ), - has_container(allured_testdir.allure_report, - has_before("child_dyn_call_fixture"), - ), - ), + ), + has_test_case( + "test_two", + has_container( + allure_results, + has_before("parent_auto_call_fixture"), + has_after("parent_auto_call_fixture::0") + ), + not_( + has_container( + allure_results, + has_before("child_manual_call_fixture"), + has_after("child_manual_call_fixture::0") + ), + ), + has_container( + allure_results, + has_before("parent_dyn_call_fixture"), + has_after("parent_dyn_call_fixture::0") + ), + has_container( + allure_results, + has_before("child_dyn_call_fixture") ) - assert_that(allured_testdir.allure_report, - has_test_case("test_three", - has_container(allured_testdir.allure_report, - has_before("parent_auto_call_fixture"), - has_after("parent_auto_call_fixture::0"), - ), - not_(has_container(allured_testdir.allure_report, - has_before("child_manual_call_fixture"), - has_after("child_manual_call_fixture::0"), - ), - ), - has_container(allured_testdir.allure_report, - has_before("parent_dyn_call_fixture"), - has_after("parent_dyn_call_fixture::0"), - ), - not_(has_container(allured_testdir.allure_report, - has_before("child_dyn_call_fixture"), - ), - ) - ) + ), + has_test_case( + "test_three", + has_container( + allure_results, + has_before("parent_auto_call_fixture"), + has_after("parent_auto_call_fixture::0") + ), + not_( + has_container( + allure_results, + has_before("child_manual_call_fixture"), + has_after("child_manual_call_fixture::0") + ), + ), + has_container( + allure_results, + has_before("parent_dyn_call_fixture"), + has_after("parent_dyn_call_fixture::0") + ), + not_( + has_container( + allure_results, + has_before("child_dyn_call_fixture") + ) ) + ) + ) + ) -def test_one_fixture_on_two_tests(allured_testdir): - allured_testdir.testdir.makepyfile(""" - import pytest - import allure +def test_one_fixture_on_two_tests(allure_pytest_runner: AllurePytestRunner): + testfile_content = ( + """ + import pytest + import allure - @pytest.fixture - def fixture(request): - with allure.step(request.node.name): - pass + @pytest.fixture + def fixture(request): + with allure.step(request.node.name): + pass - class TestClass: - def test_first(self, fixture): - pass + class TestClass: + def test_first(self, fixture): + pass - def test_second(self, fixture): - pass - """) - allured_testdir.run_with_allure() - - assert_that(allured_testdir.allure_report, - has_test_case("test_first", - has_container(allured_testdir.allure_report, - has_before("fixture", has_step("test_first")), - ), - not_(has_container(allured_testdir.allure_report, - has_before("fixture", has_step("test_second")), - )) - ), - has_test_case("test_second", - has_container(allured_testdir.allure_report, - has_before("fixture", has_step("test_second")), - ), - not_(has_container(allured_testdir.allure_report, - has_before("fixture", has_step("test_first")), - )) - ) + def test_second(self, fixture): + pass + """ + ) + allure_results = allure_pytest_runner.run_pytest(testfile_content) + + assert_that( + allure_results, + all_of( + has_test_case( + "test_first", + has_container( + allure_results, + has_before( + "fixture", + has_step("test_first") + ) + ), + not_( + has_container( + allure_results, + has_before( + "fixture", + has_step("test_second") + ) + ) + ) + ), + has_test_case( + "test_second", + has_container( + allure_results, + has_before( + "fixture", + has_step("test_second") + ) + ), + not_( + has_container( + allure_results, + has_before( + "fixture", + has_step("test_first") + ) + ) ) + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/fixture/parametrized_fixture_test.py b/tests/allure_pytest/acceptance/fixture/parametrized_fixture_test.py index 048fb86a..66bcf56a 100644 --- a/tests/allure_pytest/acceptance/fixture/parametrized_fixture_test.py +++ b/tests/allure_pytest/acceptance/fixture/parametrized_fixture_test.py @@ -1,5 +1,6 @@ -import pytest -from hamcrest import assert_that +from hamcrest import assert_that, all_of +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.container import has_container from allure_commons_test.container import has_before @@ -12,8 +13,7 @@ def params_name(request): return name -@pytest.mark.parametrize("param", [True, False]) -def test_function_scope_parametrized_fixture(param, executed_docstring_source): +def test_function_scope_parametrized_fixture(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -24,18 +24,41 @@ def test_function_scope_parametrized_fixture(param, executed_docstring_source): >>> def test_function_scope_parametrized_fixture_example(parametrized_fixture): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case(f"test_function_scope_parametrized_fixture_example[{param}]", - has_parameter("parametrized_fixture", str(param)), - has_container(executed_docstring_source.allure_report, - has_before("parametrized_fixture") - ) - ) + + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + all_of( + has_test_case( + "test_function_scope_parametrized_fixture_example[True]", + has_parameter( + "parametrized_fixture", + "True" + ), + has_container( + allure_results, + has_before("parametrized_fixture") ) + ), + has_test_case( + "test_function_scope_parametrized_fixture_example[False]", + has_parameter( + "parametrized_fixture", + "False" + ), + has_container( + allure_results, + has_before("parametrized_fixture") + ) + ) + ) + ) -@pytest.mark.parametrize("param", [True, False], ids=["param_true", "param_false"]) -def test_function_scope_parametrized_fixture_with_ids(param, executed_docstring_source, request): +def test_function_scope_parametrized_fixture_with_ids( + allure_pytest_runner: AllurePytestRunner +): """ >>> import pytest @@ -43,17 +66,30 @@ def test_function_scope_parametrized_fixture_with_ids(param, executed_docstring_ ... def parametrized_fixture(request): ... pass - >>> def test_function_scope_parametrized_fixture_with_ids_example(parametrized_fixture): + >>> def test_function(parametrized_fixture): ... pass """ - test_name = f"test_function_scope_parametrized_fixture_with_ids_example[{params_name(request)}]" + allure_results = allure_pytest_runner.run_docstring() - assert_that(executed_docstring_source.allure_report, - has_test_case(test_name, - has_parameter("parametrized_fixture", str(param)), - has_container(executed_docstring_source.allure_report, - has_before("parametrized_fixture") - ) - ) + assert_that( + allure_results, + all_of( + has_test_case( + "test_function[param_true]", + has_parameter("parametrized_fixture", "True"), + has_container( + allure_results, + has_before("parametrized_fixture") + ) + ), + has_test_case( + "test_function[param_false]", + has_parameter("parametrized_fixture", "False"), + has_container( + allure_results, + has_before("parametrized_fixture") ) + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/fixture/yield_fixture_test.py b/tests/allure_pytest/acceptance/fixture/yield_fixture_test.py index e2ac5d92..d64126f5 100644 --- a/tests/allure_pytest/acceptance/fixture/yield_fixture_test.py +++ b/tests/allure_pytest/acceptance/fixture/yield_fixture_test.py @@ -1,13 +1,16 @@ + +from hamcrest import assert_that, not_, all_of +from tests.allure_pytest.pytest_runner import AllurePytestRunner + import allure from allure_commons_test.container import has_before from allure_commons_test.container import has_container from allure_commons_test.report import has_test_case from allure_commons_test.result import has_step -from hamcrest import assert_that, not_, all_of @allure.feature("Fixture") -def test_yield_fixture(executed_docstring_source): +def test_yield_fixture(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -19,16 +22,21 @@ def test_yield_fixture(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_yield_fixture_example", - has_container(executed_docstring_source.allure_report, - has_before("yield_fixture") - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_yield_fixture_example", + has_container( + allure_results, + has_before("yield_fixture") + ) + ) + ) -def test_opened_step_function(executed_docstring_source): +def test_opened_step_function(allure_pytest_runner: AllurePytestRunner): """ >>> import allure >>> import pytest @@ -43,14 +51,16 @@ def test_opened_step_function(executed_docstring_source): ... pass """ + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, has_test_case( "test_opened_step", all_of( has_step("Body step"), has_container( - executed_docstring_source.allure_report, + allure_results, has_before( "yield_fixture", has_step( diff --git a/tests/allure_pytest/acceptance/history_id/history_id_test.py b/tests/allure_pytest/acceptance/history_id/history_id_test.py index 63bfd762..55c2ea13 100644 --- a/tests/allure_pytest/acceptance/history_id/history_id_test.py +++ b/tests/allure_pytest/acceptance/history_id/history_id_test.py @@ -1,22 +1,28 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_history_id -def test_history_id(executed_docstring_source): +def test_history_id(allure_pytest_runner: AllurePytestRunner): """ >>> def test_history_id_example(): ... assert True """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_history_id_example", - has_history_id() - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_history_id_example", + has_history_id() + ) + ) -def test_history_id_for_skipped(executed_docstring_source): +def test_history_id_for_skipped(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -25,8 +31,12 @@ def test_history_id_for_skipped(executed_docstring_source): ... assert True """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_history_id_for_skipped_example", - has_history_id() - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_history_id_for_skipped_example", + has_history_id() + ) + ) diff --git a/tests/allure_pytest/acceptance/label/bdd/bdd_label_test.py b/tests/allure_pytest/acceptance/label/bdd/bdd_label_test.py index df1c1430..6c638fc2 100644 --- a/tests/allure_pytest/acceptance/label/bdd/bdd_label_test.py +++ b/tests/allure_pytest/acceptance/label/bdd/bdd_label_test.py @@ -1,31 +1,41 @@ """ ./allure-pytest/examples/label/bdd/bdd_label.rst """ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.label import has_epic from allure_commons_test.label import has_feature from allure_commons_test.label import has_story -def test_single_bdd_label(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_single_bdd_label", - has_epic("My epic"), - has_feature("My feature"), - has_story("My story") - ) - ) - - -def test_multiple_bdd_label(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_multiple_bdd_label", - has_epic("My epic"), - has_epic("Another epic"), - has_feature("My feature"), - has_feature("Another feature"), - has_feature("One more feature"), - has_story("My story"), - has_story("Alternative story") - ) - ) +def test_single_bdd_label(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_single_bdd_label", + has_epic("My epic"), + has_feature("My feature"), + has_story("My story") + ) + ) + + +def test_multiple_bdd_label(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_multiple_bdd_label", + has_epic("My epic"), + has_epic("Another epic"), + has_feature("My feature"), + has_feature("Another feature"), + has_feature("One more feature"), + has_story("My story"), + has_story("Alternative story") + ) + ) diff --git a/tests/allure_pytest/acceptance/label/bdd/dynamic_bdd_label_test.py b/tests/allure_pytest/acceptance/label/bdd/dynamic_bdd_label_test.py index e3a9f51c..66c30a30 100644 --- a/tests/allure_pytest/acceptance/label/bdd/dynamic_bdd_label_test.py +++ b/tests/allure_pytest/acceptance/label/bdd/dynamic_bdd_label_test.py @@ -2,43 +2,64 @@ import pytest from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.label import has_feature, has_epic, has_story -def test_dynamic_labels(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_dynamic_labels", - has_feature("first feature"), - has_feature("second feature"), - has_epic("first epic"), - has_epic("second epic"), - has_story("first story"), - has_story("second story"), - ) - ) - - -@pytest.mark.parametrize("feature, epic, story", [("first feature", "first epic", "first story"), - ("second feature", "second epic", "second story")]) -def test_parametrized_dynamic_labels(executed_docstring_path, feature, epic, story): - assert_that(executed_docstring_path.allure_report, - has_test_case(f"test_parametrized_dynamic_labels[{feature}-{epic}-{story}]", - has_feature(feature), - has_epic(epic), - has_story(story), - ) - ) - - -def test_multiple_dynamic_labels(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_multiple_dynamic_labels", - has_feature("first feature"), - has_feature("second feature"), - has_epic("first epic"), - has_epic("second epic"), - has_story("first story"), - has_story("second story"), - ) - ) +def test_dynamic_labels(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_dynamic_labels", + has_feature("first feature"), + has_feature("second feature"), + has_epic("first epic"), + has_epic("second epic"), + has_story("first story"), + has_story("second story") + ) + ) + + +@pytest.mark.parametrize("feature, epic, story", [ + pytest.param("first feature", "first epic", "first story", id="first"), + pytest.param("second feature", "second epic", "second story", id="second") +]) +def test_parametrized_dynamic_labels( + allure_pytest_runner: AllurePytestRunner, + feature, + epic, + story +): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + f"test_parametrized_dynamic_labels[{feature}-{epic}-{story}]", + has_feature(feature), + has_epic(epic), + has_story(story) + ) + ) + + +def test_multiple_dynamic_labels(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_multiple_dynamic_labels", + has_feature("first feature"), + has_feature("second feature"), + has_epic("first epic"), + has_epic("second epic"), + has_story("first story"), + has_story("second story") + ) + ) diff --git a/tests/allure_pytest/acceptance/label/bdd/select_bdd_test.py b/tests/allure_pytest/acceptance/label/bdd/select_bdd_test.py index 7faeff29..dfa4befc 100644 --- a/tests/allure_pytest/acceptance/label/bdd/select_bdd_test.py +++ b/tests/allure_pytest/acceptance/label/bdd/select_bdd_test.py @@ -2,50 +2,61 @@ import pytest from hamcrest import assert_that, only_contains, any_of, ends_with +from tests.allure_pytest.pytest_runner import AllurePytestRunner @pytest.mark.parametrize( ["options", "expected_tests"], [ - ({"epics": ["Another Epic"]}, - [ - "test_with_another_epic_feature_story" - ]), - - ({"features": ["My Feature"]}, - [ - "test_with_epic_feature_story", - "test_with_epic_feature" - ]), - - ({"stories": ["My Story", "Another Story"]}, - [ - "test_with_epic_feature_story", - "test_with_another_epic_feature_story" - ]), - - ({"stories": ["My Story"], "epics": ["Another Epic"]}, - [ - "test_with_epic_feature_story", - "test_with_another_epic_feature_story" - ]) + pytest.param( + {"epics": ["Another Epic"]}, + ["test_with_another_epic_feature_story"], + id="epics" + ), + + pytest.param( + {"features": ["My Feature"]}, + [ + "test_with_epic_feature_story", + "test_with_epic_feature" + ], + id="features" + ), + + pytest.param( + {"stories": ["My Story", "Another Story"]}, + [ + "test_with_epic_feature_story", + "test_with_another_epic_feature_story" + ], + id="stories" + ), + + pytest.param( + {"stories": ["My Story"], "epics": ["Another Epic"]}, + [ + "test_with_epic_feature_story", + "test_with_another_epic_feature_story" + ], + id="story-or-epic" + ) ] ) -def test_select_by_bdd_label(allured_testdir, options, expected_tests): - allured_testdir.parse_docstring_path() - - params = [] - - for key in options.keys(): - params.append(f"--allure-{key}") - params.append(",".join(options[key])) - - allured_testdir.run_with_allure(*params) - test_cases = [test_case["fullName"] for test_case in allured_testdir.allure_report.test_cases] - +def test_select_by_bdd_label( + allure_pytest_runner: AllurePytestRunner, + options, + expected_tests +): + bdd_filter_args = ( + f"--allure-{k}=" + ",".join(v) for k, v in options.items() + ) + + allure_results = allure_pytest_runner.run_docpath_examples(*bdd_filter_args) + + test_cases = [test_case["fullName"] for test_case in allure_results.test_cases] assert_that(test_cases, only_contains( any_of( - *[ends_with(name) for name in expected_tests] + *(ends_with(name) for name in expected_tests) ) )) diff --git a/tests/allure_pytest/acceptance/label/custom/custom_label_test.py b/tests/allure_pytest/acceptance/label/custom/custom_label_test.py index 3e943d43..e3b2ae9e 100644 --- a/tests/allure_pytest/acceptance/label/custom/custom_label_test.py +++ b/tests/allure_pytest/acceptance/label/custom/custom_label_test.py @@ -1,14 +1,20 @@ """ ./allure-pytest/examples/label/custom/custom_label.rst """ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.label import has_label -def test_custom_label(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_custom_label", - has_label("Application", "desktop"), - has_label("Application", "mobile") - ) - ) +def test_custom_label(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples() + + assert_that( + allure_results, + has_test_case( + "test_custom_label", + has_label("Application", "desktop"), + has_label("Application", "mobile") + ) + ) diff --git a/tests/allure_pytest/acceptance/label/custom/select_custom_label_test.py b/tests/allure_pytest/acceptance/label/custom/select_custom_label_test.py index 02cbdcc9..db4c643b 100644 --- a/tests/allure_pytest/acceptance/label/custom/select_custom_label_test.py +++ b/tests/allure_pytest/acceptance/label/custom/select_custom_label_test.py @@ -1,49 +1,64 @@ """ ./allure-pytest/examples/label/custom/select_tests_by_label.rst """ import pytest -from hamcrest import assert_that, ends_with, contains_inanyorder +from hamcrest import assert_that, all_of +from tests.allure_pytest.pytest_runner import AllurePytestRunner + +from allure_commons_test.report import has_test_case @pytest.mark.parametrize( ["labels", "expected_tests"], [ - ( + pytest.param( {"Application": ["desktop"]}, [ "test_custom_label_one", "test_custom_label_both" - ] + ], + id="desktop" ), - ( + pytest.param( {"Application": ["mobile"]}, [ "test_custom_label_another", "test_custom_label_both" - ] + ], + id="mobile" ), - ( + pytest.param( {"Application": ["desktop", "mobile"]}, [ "test_custom_label_one", "test_custom_label_another", "test_custom_label_both" - ] + ], + id="desktop-or-mobile" ), - ( + pytest.param( {"Application": ["mobile"], "layer": ["api"]}, [ "test_custom_label_another", "test_custom_label_both", "test_layer_label" - ] + ], + id="mobile-or-api" ) ] ) -def test_select_by_custom_label(allured_testdir, labels, expected_tests): - allured_testdir.parse_docstring_path() - allure_labels = [] - for label_name, label_values in labels.items(): - allure_labels.extend(["--allure-label", f"{label_name}={','.join(label_values)}"]) - allured_testdir.run_with_allure(*allure_labels) - test_cases = [test_case["fullName"] for test_case in allured_testdir.allure_report.test_cases] - assert_that(test_cases, contains_inanyorder(*[ends_with(name) for name in expected_tests])) +def test_select_by_custom_label( + allure_pytest_runner: AllurePytestRunner, + labels, + expected_tests +): + label_filters = ( + f"--allure-label={k}=" + ",".join(v) for k, v in labels.items() + ) + allure_results = allure_pytest_runner.run_docpath_examples(*label_filters) + + assert_that( + allure_results, + all_of( + *map(has_test_case, expected_tests) + ) + ) diff --git a/tests/allure_pytest/acceptance/label/id/set_testcase_id_test.py b/tests/allure_pytest/acceptance/label/id/set_testcase_id_test.py index 72e6b9b3..2dccfc20 100644 --- a/tests/allure_pytest/acceptance/label/id/set_testcase_id_test.py +++ b/tests/allure_pytest/acceptance/label/id/set_testcase_id_test.py @@ -1,9 +1,11 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.label import has_label -def test_set_testcase_id_label(executed_docstring_source): +def test_set_testcase_id_label(allure_pytest_runner: AllurePytestRunner): """ >>> import allure @@ -11,22 +13,32 @@ def test_set_testcase_id_label(executed_docstring_source): ... def test_allure_ee_id_label_example(): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_allure_ee_id_label_example", - has_label("as_id", 123), - ) - ) + + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_allure_ee_id_label_example", + has_label("as_id", 123), + ) + ) -def test_set_dynamic_testcase_id_label(executed_docstring_source): +def test_set_dynamic_testcase_id_label(allure_pytest_runner: AllurePytestRunner): """ >>> import allure >>> def test_allure_ee_id_dynamic_label_example(): ... allure.dynamic.id(345) """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_allure_ee_id_dynamic_label_example", - has_label("as_id", 345), - ) - ) + + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_allure_ee_id_dynamic_label_example", + has_label("as_id", 345), + ) + ) diff --git a/tests/allure_pytest/acceptance/label/manual/manual_test.py b/tests/allure_pytest/acceptance/label/manual/manual_test.py index efac2362..2752cc5d 100644 --- a/tests/allure_pytest/acceptance/label/manual/manual_test.py +++ b/tests/allure_pytest/acceptance/label/manual/manual_test.py @@ -1,21 +1,31 @@ """ ./allure-pytest/examples/label/manual/allure_manual.rst """ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.label import has_label -def test_allure_manual_label(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_manual", - has_label("ALLURE_MANUAL", True) - ) - ) +def test_allure_manual_label(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_manual", + has_label("ALLURE_MANUAL", True), + ) + ) + +def test_allure_manual_label_dynamic(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) -def test_allure_manual_label_dynamic(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_manual_dynamic", - has_label("ALLURE_MANUAL", True) - ), - ) + assert_that( + allure_results, + has_test_case( + "test_manual_dynamic", + has_label("ALLURE_MANUAL", True), + ) + ) diff --git a/tests/allure_pytest/acceptance/label/package/regression_test.py b/tests/allure_pytest/acceptance/label/package/regression_test.py index a53a1a98..42c90d31 100644 --- a/tests/allure_pytest/acceptance/label/package/regression_test.py +++ b/tests/allure_pytest/acceptance/label/package/regression_test.py @@ -1,46 +1,45 @@ import textwrap from hamcrest import assert_that -from hamcrest import ends_with -from tests.conftest import AlluredTestdir +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.label import has_package -def test_path_with_dots_test(allured_testdir: AlluredTestdir): - path = allured_testdir.testdir.mkpydir("path.with.dots") - - path.joinpath("test_path.py").write_text( +def test_path_with_dots_test(allure_pytest_runner: AllurePytestRunner): + package_path = allure_pytest_runner.pytester.mkpydir("path.with.dots") + package_path.joinpath("test_path.py").write_text( textwrap.dedent( - """\ + """ def test_path_with_dots_test_example(): pass """ ) ) - allured_testdir.run_with_allure() + allure_results = allure_pytest_runner.run_pytest() assert_that( - allured_testdir.allure_report, + allure_results, has_test_case( "test_path_with_dots_test_example", - has_package(ends_with("path.with.dots.test_path")) + has_package("path.with.dots.test_path") ) ) -def test_with_no_package(allured_testdir: AlluredTestdir): +def test_with_no_package(allure_pytest_runner: AllurePytestRunner): """ >>> def test_package_less(request): ... pass """ - allured_testdir.parse_docstring_source() - allured_testdir.testdir.makeini("""[pytest]""") - allured_testdir.run_with_allure(allured_testdir.testdir.path) + allure_pytest_runner.pytester.makeini("""[pytest]""") + + allure_results = allure_pytest_runner.run_docstring() assert_that( - allured_testdir.allure_report, + allure_results, has_test_case( "test_package_less", has_package("test_with_no_package") diff --git a/tests/allure_pytest/acceptance/label/severity/class_severity_test.py b/tests/allure_pytest/acceptance/label/severity/class_severity_test.py index f6e330d8..6708385d 100644 --- a/tests/allure_pytest/acceptance/label/severity/class_severity_test.py +++ b/tests/allure_pytest/acceptance/label/severity/class_severity_test.py @@ -1,41 +1,71 @@ """./allure-pytest/examples/label/severity/class_severity.rst""" from hamcrest import assert_that, all_of, is_not +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.label import has_severity -def test_decorated_class_not_decorated_method(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("TestDecoratedClass#test_not_decorated_method", - has_severity("trivial") - ) - ) +def test_decorated_class_not_decorated_method( + allure_pytest_runner: AllurePytestRunner +): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + assert_that( + allure_results, + has_test_case( + "TestDecoratedClass#test_not_decorated_method", + has_severity("trivial") + ) + ) -def test_decorated_class_decorated_method(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("TestDecoratedClass#test_decorated_method", - all_of(has_severity("minor"), - is_not(has_severity("trivial")) - ) - ) - ) +def test_decorated_class_decorated_method( + allure_pytest_runner: AllurePytestRunner +): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "TestDecoratedClass#test_decorated_method", + all_of( + has_severity("minor"), + is_not(has_severity("trivial")) + ) + ) + ) + + +def test_not_decorated_sub_class_not_decorated_method( + allure_pytest_runner: AllurePytestRunner +): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "TestNotDecoratedSubClass#test_not_decorated_method", + has_severity("trivial") + ) + ) -def test_not_decorated_sub_class_not_decorated_method(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("TestNotDecoratedSubClass#test_not_decorated_method", - has_severity("trivial") - ) - ) +def test_not_decorated_sub_class_decorated_method( + allure_pytest_runner: AllurePytestRunner +): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) -def test_not_decorated_sub_class_decorated_method(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("TestNotDecoratedSubClass#test_decorated_method", - all_of(has_severity("critical"), - is_not(has_severity("trivial")) - ) - ) + assert_that( + allure_results, + has_test_case( + "TestNotDecoratedSubClass#test_decorated_method", + all_of( + has_severity("critical"), + is_not( + has_severity("trivial") ) + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/label/severity/module_severity_test.py b/tests/allure_pytest/acceptance/label/severity/module_severity_test.py index 81e77be5..606633a4 100644 --- a/tests/allure_pytest/acceptance/label/severity/module_severity_test.py +++ b/tests/allure_pytest/acceptance/label/severity/module_severity_test.py @@ -1,37 +1,55 @@ """ ./allure-pytest/examples/label/severity/module_severity.rst """ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.label import has_severity -def test_not_decorated_function(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_not_decorated_function", - has_severity("trivial") - ) - ) +def test_not_decorated_function(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_not_decorated_function", + has_severity("trivial") + ) + ) + + +def test_decorated_function(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_decorated_function", + has_severity("minor") + ) + ) -def test_decorated_function(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_decorated_function", - has_severity("minor") - ) - ) +def test_method_of_not_decorated_class(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + assert_that( + allure_results, + has_test_case( + "test_method_of_not_decorated_class", + has_severity("trivial") + ) + ) -def test_method_of_not_decorated_class(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_method_of_not_decorated_class", - has_severity("trivial") - ) - ) +def test_method_of_decorated_class(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) -def test_method_of_decorated_class(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_method_of_decorated_class", - has_severity("normal") - ) - ) + assert_that( + allure_results, + has_test_case( + "test_method_of_decorated_class", + has_severity("normal") + ) + ) diff --git a/tests/allure_pytest/acceptance/label/severity/select_severity_test.py b/tests/allure_pytest/acceptance/label/severity/select_severity_test.py index 85fce270..3cd3878c 100644 --- a/tests/allure_pytest/acceptance/label/severity/select_severity_test.py +++ b/tests/allure_pytest/acceptance/label/severity/select_severity_test.py @@ -2,6 +2,7 @@ import pytest from hamcrest import assert_that, only_contains, any_of, ends_with +from tests.allure_pytest.pytest_runner import AllurePytestRunner @pytest.mark.parametrize( @@ -33,11 +34,19 @@ ) ] ) -def test_select_by_severity_level(allured_testdir, severities, expected_tests): - allured_testdir.parse_docstring_path() +def test_select_by_severity_level( + allure_pytest_runner: AllurePytestRunner, + severities, + expected_tests +): + allure_results = allure_pytest_runner.run_docpath_examples( + "--allure-severities", + ",".join(severities) + ) - allured_testdir.run_with_allure("--allure-severities", ",".join(severities)) - test_cases = [test_case["fullName"] for test_case in allured_testdir.allure_report.test_cases] + test_cases = [ + test_case["fullName"] for test_case in allure_results.test_cases + ] assert_that(test_cases, only_contains( any_of( diff --git a/tests/allure_pytest/acceptance/label/severity/severity_test.py b/tests/allure_pytest/acceptance/label/severity/severity_test.py index 8391b31d..42665734 100644 --- a/tests/allure_pytest/acceptance/label/severity/severity_test.py +++ b/tests/allure_pytest/acceptance/label/severity/severity_test.py @@ -1,13 +1,19 @@ """ ./allure-pytest/examples/label/severity/severity.rst """ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.label import has_severity -def test_severity(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_severity", - has_severity("minor"), - ) - ) +def test_severity(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples() + + assert_that( + allure_results, + has_test_case( + "test_severity", + has_severity("minor"), + ) + ) diff --git a/tests/allure_pytest/acceptance/label/suite/custom_suite.py b/tests/allure_pytest/acceptance/label/suite/custom_suite.py deleted file mode 100644 index 2914cc5d..00000000 --- a/tests/allure_pytest/acceptance/label/suite/custom_suite.py +++ /dev/null @@ -1,15 +0,0 @@ -""" ./allure-pytest/examples/label/suite/custom_suite.rst """ - -from hamcrest import assert_that -from allure_commons_test.report import has_test_case -from allure_commons_test.label import has_suite, has_parent_suite, has_sub_suite - - -def test_custom_suite(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_custom_suite", - has_suite("suite name"), - has_parent_suite("parent suite name"), - has_sub_suite("sub suite name") - ) - ) diff --git a/tests/allure_pytest/acceptance/label/suite/custom_suite_test.py b/tests/allure_pytest/acceptance/label/suite/custom_suite_test.py new file mode 100644 index 00000000..98abfc57 --- /dev/null +++ b/tests/allure_pytest/acceptance/label/suite/custom_suite_test.py @@ -0,0 +1,21 @@ +""" ./allure-pytest/examples/label/suite/custom_suite.rst """ + +from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + +from allure_commons_test.report import has_test_case +from allure_commons_test.label import has_suite, has_parent_suite, has_sub_suite + + +def test_custom_suite(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples() + + assert_that( + allure_results, + has_test_case( + "test_custom_suite", + has_suite("suite name"), + has_parent_suite("parent suite name"), + has_sub_suite("sub suite name") + ) + ) diff --git a/tests/allure_pytest/acceptance/label/suite/default_suite_test.py b/tests/allure_pytest/acceptance/label/suite/default_suite_test.py index 63260189..92a71fbe 100644 --- a/tests/allure_pytest/acceptance/label/suite/default_suite_test.py +++ b/tests/allure_pytest/acceptance/label/suite/default_suite_test.py @@ -1,51 +1,36 @@ from doctest import script_from_examples -from tests.conftest import AlluredTestdir from hamcrest import assert_that, anything, not_ +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.label import has_parent_suite from allure_commons_test.label import has_suite from allure_commons_test.label import has_sub_suite -def test_default_suites_no_parent_module(executed_docstring_source): +def test_no_parent_module( + allure_pytest_runner: AllurePytestRunner +): """ >>> def test_default_suite_example(): ... pass """ + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, has_test_case( "test_default_suite_example", not_(has_parent_suite(anything())), - has_suite("test_default_suites_no_parent_module"), + has_suite("test_no_parent_module"), not_(has_sub_suite(anything())) ) ) -def test_default_suites_class_no_parent_module(executed_docstring_source): - """ - >>> class TestSuiteClass: - ... def test_default_class_suite_example(self): - ... pass - - """ - - assert_that( - executed_docstring_source.allure_report, - has_test_case( - "test_default_class_suite_example", - not_(has_parent_suite(anything())), - has_suite("test_default_suites_class_no_parent_module"), - has_sub_suite("TestSuiteClass") - ) - ) - - -def test_default_suites_with_class_and_parent_module( - docstring: str, - allured_testdir: AlluredTestdir +def test_class_no_parent_module( + allure_pytest_runner: AllurePytestRunner ): """ >>> class TestSuiteClass: @@ -54,49 +39,41 @@ def test_default_suites_with_class_and_parent_module( """ - content = script_from_examples(docstring) - allured_testdir.testdir.makepyfile( - **{ - "parent_module/test_default_suites_with_parent_module.py": content - } - ) - allured_testdir.run_with_allure() + allure_results = allure_pytest_runner.run_docstring() assert_that( - allured_testdir.allure_report, + allure_results, has_test_case( "test_default_class_suite_example", - has_parent_suite("parent_module"), - has_suite("test_default_suites_with_parent_module"), + not_(has_parent_suite(anything())), + has_suite("test_class_no_parent_module"), has_sub_suite("TestSuiteClass") ) ) -def test_default_suites_with_parent_module( - docstring: str, - allured_testdir: AlluredTestdir +def test_with_parent_module( + allure_pytest_runner: AllurePytestRunner, + docstring ): """ - >>> def test_default_class_suite_example(self): + >>> def test_default_suite_example(): ... pass """ content = script_from_examples(docstring) - module = "test_default_suites_with_class_and_parent_module" + module = "test_default_suites_with_parent_module" filename = module + ".py" fullname = "parent_module/" + filename - allured_testdir.testdir.makefile( - ".py", - **{fullname: content } - ) - allured_testdir.run_with_allure() + allure_pytest_runner.pytester.makepyfile(**{fullname: content}) + + allure_results = allure_pytest_runner.run_pytest() assert_that( - allured_testdir.allure_report, + allure_results, has_test_case( - "test_default_class_suite_example", + "test_default_suite_example", has_parent_suite("parent_module"), has_suite(module), not_(has_sub_suite(anything())) @@ -104,9 +81,9 @@ def test_default_suites_with_parent_module( ) -def test_default_suites_with_class_and_parent_module( - docstring: str, - allured_testdir: AlluredTestdir +def test_with_class_and_parent_module( + allure_pytest_runner: AllurePytestRunner, + docstring ): """ >>> class TestSuiteClass: @@ -119,14 +96,12 @@ def test_default_suites_with_class_and_parent_module( module = "test_default_suites_with_class_and_parent_module" filename = module + ".py" fullname = "parent_module/" + filename - allured_testdir.testdir.makefile( - ".py", - **{fullname: content } - ) - allured_testdir.run_with_allure() + allure_pytest_runner.pytester.makepyfile(**{fullname: content}) + + allure_results = allure_pytest_runner.run_pytest() assert_that( - allured_testdir.allure_report, + allure_results, has_test_case( "test_default_class_suite_example", has_parent_suite("parent_module"), diff --git a/tests/allure_pytest/acceptance/label/suite/module_level_custom_suite_test.py b/tests/allure_pytest/acceptance/label/suite/module_level_custom_suite_test.py index 7db7d43e..edf1d1ec 100644 --- a/tests/allure_pytest/acceptance/label/suite/module_level_custom_suite_test.py +++ b/tests/allure_pytest/acceptance/label/suite/module_level_custom_suite_test.py @@ -1,13 +1,19 @@ """ ./allure-pytest/examples/label/suite/module_level_custom_suite.rst """ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.label import has_suite -def test_module_custom_suite(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_module_level_custom_suite", - has_suite("module level suite name"), - ) - ) +def test_module_custom_suite(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples() + + assert_that( + allure_results, + has_test_case( + "test_module_level_custom_suite", + has_suite("module level suite name"), + ) + ) diff --git a/tests/allure_pytest/acceptance/label/tag/tag_test.py b/tests/allure_pytest/acceptance/label/tag/tag_test.py index 0636e5cb..3f32475b 100644 --- a/tests/allure_pytest/acceptance/label/tag/tag_test.py +++ b/tests/allure_pytest/acceptance/label/tag/tag_test.py @@ -1,9 +1,11 @@ from hamcrest import assert_that, not_ +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.label import has_tag -def test_pytest_marker(executed_docstring_source): +def test_pytest_marker(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -13,15 +15,21 @@ def test_pytest_marker(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_pytest_marker_example", - has_tag("cool"), - has_tag("stuff") - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_pytest_marker_example", + has_tag("cool"), + has_tag("stuff") + ) + ) -def test_show_reserved_pytest_markers_full_decorator(executed_docstring_source): +def test_show_reserved_pytest_markers_full_decorator( + allure_pytest_runner: AllurePytestRunner +): """ >>> import pytest @@ -34,18 +42,26 @@ def test_show_reserved_pytest_markers_full_decorator(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case('test_show_reserved_pytest_markers_full_decorator_example[foo]', - has_tag("usermark1"), - has_tag("usermark2"), - has_tag("@pytest.mark.skipif(False, reason='reason1')"), - not_(has_tag("@pytest.mark.skipif(False, reason='reason2')")), - not_(has_tag("@pytest.mark.parametrize('param', ['foo'])")) - ) - ) - - -def test_pytest_xfail_marker(executed_docstring_source): + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_show_reserved_pytest_markers_full_decorator_example[foo]", + has_tag("usermark1"), + has_tag("usermark2"), + has_tag("@pytest.mark.skipif(False, reason='reason1')"), + not_( + has_tag("@pytest.mark.skipif(False, reason='reason2')") + ), + not_( + has_tag("@pytest.mark.parametrize('param', ['foo'])") + ) + ) + ) + + +def test_pytest_xfail_marker(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -54,14 +70,18 @@ def test_pytest_xfail_marker(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case('test_pytest_xfail_marker_example', - has_tag("@pytest.mark.xfail(reason='this is unexpect pass')"), - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_pytest_xfail_marker_example", + has_tag("@pytest.mark.xfail(reason='this is unexpect pass')") + ) + ) -def test_pytest_marker_with_args(executed_docstring_source): +def test_pytest_marker_with_args(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -70,14 +90,18 @@ def test_pytest_marker_with_args(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_pytest_marker_with_args_example", - has_tag("marker('cool', 'stuff')") - ) - ) + allure_results = allure_pytest_runner.run_docstring() + assert_that( + allure_results, + has_test_case( + "test_pytest_marker_with_args_example", + has_tag("marker('cool', 'stuff')") + ) + ) -def test_pytest_marker_with_kwargs(executed_docstring_source): + +def test_pytest_marker_with_kwargs(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -86,14 +110,20 @@ def test_pytest_marker_with_kwargs(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_pytest_marker_with_kwargs_example", - has_tag("marker(stuff='cool')") - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_pytest_marker_with_kwargs_example", + has_tag("marker(stuff='cool')") + ) + ) -def test_pytest_marker_with_kwargs_native_encoding(executed_docstring_source): +def test_pytest_marker_with_kwargs_native_encoding( + allure_pytest_runner: AllurePytestRunner +): """ >>> import pytest @@ -102,14 +132,20 @@ def test_pytest_marker_with_kwargs_native_encoding(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_pytest_marker_with_kwargs_native_encoding_example", - has_tag("marker(stuff='я')") - ) - ) + allure_results = allure_pytest_runner.run_docstring() + assert_that( + allure_results, + has_test_case( + "test_pytest_marker_with_kwargs_native_encoding_example", + has_tag("marker(stuff='я')") + ) + ) -def test_pytest_marker_with_kwargs_utf_encoding(executed_docstring_source): + +def test_pytest_marker_with_kwargs_utf_encoding( + allure_pytest_runner: AllurePytestRunner +): """ >>> import pytest @@ -118,8 +154,12 @@ def test_pytest_marker_with_kwargs_utf_encoding(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_pytest_marker_with_kwargs_utf_encoding_example", - has_tag("marker(stuff='я')") - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_pytest_marker_with_kwargs_utf_encoding_example", + has_tag("marker(stuff='я')") + ) + ) diff --git a/tests/allure_pytest/acceptance/link/dynamic_link_test.py b/tests/allure_pytest/acceptance/link/dynamic_link_test.py index 90f33f01..d8390a77 100644 --- a/tests/allure_pytest/acceptance/link/dynamic_link_test.py +++ b/tests/allure_pytest/acceptance/link/dynamic_link_test.py @@ -1,40 +1,58 @@ """ ./allure-pytest/examples/link/dynamic_link.rst """ -import pytest -from hamcrest import assert_that, equal_to +from hamcrest import assert_that, equal_to, all_of +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_link from allure_commons_test.result import has_issue_link -def test_dynamic_link(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_dynamic_link", - has_issue_link("issues/24") - ) - ) +def test_dynamic_link(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_dynamic_link", + has_issue_link("issues/24") + ) + ) + +def test_parametrize_dynamic_link(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) -@pytest.mark.parametrize("link", ["issues/24", "issues/132"]) -def test_parametrize_dynamic_link(executed_docstring_path, link): - assert_that(executed_docstring_path.allure_report, - has_test_case(f"test_parametrize_dynamic_link[{link}]", - has_issue_link(link), - ) - ) + assert_that( + allure_results, + all_of( + has_test_case( + "test_parametrize_dynamic_link[issues/24]", + has_issue_link("issues/24"), + ), + has_test_case( + "test_parametrize_dynamic_link[issues/132]", + has_issue_link("issues/132"), + ) + ) + ) -def test_all_links_together(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_all_links_together", - has_issue_link("issues/24"), - has_issue_link("issues/132"), - has_link("allure", name="QAMETA", link_type="docs") - ) - ) +def test_all_links_together(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + assert_that( + allure_results, + has_test_case( + "test_all_links_together", + has_issue_link("issues/24"), + has_issue_link("issues/132"), + has_link("allure", name="QAMETA", link_type="docs") + ) + ) -def test_unique_dynamic_links(executed_docstring_source): + +def test_unique_dynamic_links(allure_pytest_runner: AllurePytestRunner): """ >>> import allure @@ -42,5 +60,14 @@ def test_unique_dynamic_links(executed_docstring_source): ... allure.dynamic.link("some/unique/dynamic/link") ... allure.dynamic.link("some/unique/dynamic/link") """ - assert_that(executed_docstring_source.allure_report.test_cases[0]['links'], - equal_to([{'url': 'some/unique/dynamic/link', 'type': 'link', 'name': 'some/unique/dynamic/link'}])) + + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results.test_cases[0]['links'], + equal_to([{ + 'url': 'some/unique/dynamic/link', + 'type': 'link', + 'name': 'some/unique/dynamic/link' + }]) + ) diff --git a/tests/allure_pytest/acceptance/link/link_pattern_test.py b/tests/allure_pytest/acceptance/link/link_pattern_test.py index a91b90c5..d1fa6bc8 100644 --- a/tests/allure_pytest/acceptance/link/link_pattern_test.py +++ b/tests/allure_pytest/acceptance/link/link_pattern_test.py @@ -1,14 +1,14 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_link, has_issue_link -def test_link_pattern(allured_testdir): +def test_link_pattern(allure_pytest_runner: AllurePytestRunner): """ ./allure-pytest/examples/link/dynamic_link.rst """ - allured_testdir.parse_docstring_path() - - allured_testdir.run_with_allure( + allure_results = allure_pytest_runner.run_docpath_examples( "--allure-link-pattern", "issue:https://github.com/allure-framework/allure-python2/{}", "--allure-link-pattern", @@ -16,7 +16,7 @@ def test_link_pattern(allured_testdir): ) assert_that( - allured_testdir.allure_report, + allure_results, has_test_case( "test_all_links_together", has_issue_link("https://github.com/allure-framework/allure-python2/issues/24"), diff --git a/tests/allure_pytest/acceptance/link/link_test.py b/tests/allure_pytest/acceptance/link/link_test.py index d359cc03..1994dd31 100644 --- a/tests/allure_pytest/acceptance/link/link_test.py +++ b/tests/allure_pytest/acceptance/link/link_test.py @@ -1,39 +1,57 @@ """ ./allure-pytest/examples/link/link.rst """ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_link from allure_commons_test.result import has_issue_link from allure_commons_test.result import has_test_case_link -def test_link(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_link", - has_link("http://qameta.io") - ) - ) +def test_link(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_link", + has_link("http://qameta.io") + ) + ) + + +def test_issue_link(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_issue_link", + has_issue_link("https://github.com/allure-framework/allure-python/issues/24") + ) + ) -def test_issue_link(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_issue_link", - has_issue_link("https://github.com/allure-framework/allure-python/issues/24") - ) - ) +def test_testcase_link(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + assert_that( + allure_results, + has_test_case( + "test_testcase_link", + has_test_case_link("issues/24#issuecomment-277330977") + ) + ) -def test_testcase_link(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_testcase_link", - has_test_case_link("issues/24#issuecomment-277330977") - ) - ) +def test_custom_link(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) -def test_custom_link(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_custom_link", - has_link("http://qameta.io", name="QAMETA", link_type="homepage") - ) - ) + assert_that( + allure_results, + has_test_case( + "test_custom_link", + has_link("http://qameta.io", name="QAMETA", link_type="homepage") + ) + ) diff --git a/tests/allure_pytest/acceptance/parametrization/metafunc_test.py b/tests/allure_pytest/acceptance/parametrization/metafunc_test.py index 620f133a..3e63b4cf 100644 --- a/tests/allure_pytest/acceptance/parametrization/metafunc_test.py +++ b/tests/allure_pytest/acceptance/parametrization/metafunc_test.py @@ -1,11 +1,11 @@ -import pytest -from hamcrest import assert_that +from hamcrest import assert_that, all_of +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_parameter -@pytest.mark.parametrize("param", [True, False]) -def test_metafunc_param(executed_docstring_source, param): +def test_metafunc_param(allure_pytest_runner: AllurePytestRunner): """ >>> def pytest_generate_tests(metafunc): ... if "metafunc_param" in metafunc.fixturenames: @@ -16,28 +16,50 @@ def test_metafunc_param(executed_docstring_source, param): ... assert metafunc_param """ - assert_that(executed_docstring_source.allure_report, - has_test_case(f"test_metafunc_param_example[{param}]", - has_parameter("metafunc_param", str(param)) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + all_of( + has_test_case( + "test_metafunc_param_example[True]", + has_parameter("metafunc_param", "True") + ), + has_test_case( + "test_metafunc_param_example[False]", + has_parameter("metafunc_param", "False") + ) + ) + ) -@pytest.mark.parametrize("param", [True, False]) -def test_metafunc_param_with_ids(executed_docstring_source, param): +def test_metafunc_param_with_ids(allure_pytest_runner: AllurePytestRunner): """ >>> def pytest_generate_tests(metafunc): ... if "metafunc_param_with_ids" in metafunc.fixturenames: - ... metafunc.parametrize("metafunc_param_with_ids", [True, False], ids=["pass", "fail"]) + ... metafunc.parametrize( + ... "metafunc_param_with_ids", + ... [True, False], + ... ids=["pass", "fail"] + ... ) >>> def test_metafunc_param_with_ids_example(metafunc_param_with_ids): ... assert metafunc_param_with_ids """ - param_name = "pass" if param else "fail" - assert_that(executed_docstring_source.allure_report, - has_test_case(f"test_metafunc_param_with_ids_example[{param_name}]", - has_parameter("metafunc_param_with_ids", str(param)) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + all_of( + has_test_case( + "test_metafunc_param_with_ids_example[pass]", + has_parameter("metafunc_param_with_ids", "True") + ), + has_test_case( + "test_metafunc_param_with_ids_example[fail]", + has_parameter("metafunc_param_with_ids", "False") + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/parametrization/parametrization_test.py b/tests/allure_pytest/acceptance/parametrization/parametrization_test.py index 8ff611df..07d7ac3b 100644 --- a/tests/allure_pytest/acceptance/parametrization/parametrization_test.py +++ b/tests/allure_pytest/acceptance/parametrization/parametrization_test.py @@ -1,18 +1,13 @@ -import pytest from hamcrest import assert_that, has_entry, ends_with, all_of +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_parameter from allure_commons_test.result import with_excluded from allure_commons_test.result import with_mode -def params_name(request): - node_id = request.node.nodeid - _, name = node_id.rstrip("]").split("[") - return name - - -def test_parametrization(executed_docstring_source): +def test_parametrization(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -21,8 +16,10 @@ def test_parametrization(executed_docstring_source): ... assert param """ + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, all_of( has_test_case( "test_parametrization_example[1]", @@ -36,7 +33,7 @@ def test_parametrization(executed_docstring_source): ) -def test_parametrization_with_ids(executed_docstring_source): +def test_parametrization_with_ids(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -45,22 +42,24 @@ def test_parametrization_with_ids(executed_docstring_source): ... pass """ + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, all_of( has_test_case( - f"test_parametrization_with_ids_example[a]", + "test_parametrization_with_ids_example[a]", has_parameter("v", "1") ), has_test_case( - f"test_parametrization_with_ids_example[b]", + "test_parametrization_with_ids_example[b]", has_parameter("v", "2") ) ) ) -def test_parametrization_many_decorators(executed_docstring_source): +def test_parametrization_many_decorators(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -70,8 +69,10 @@ def test_parametrization_many_decorators(executed_docstring_source): ... pass """ + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, all_of( has_test_case( "test_parametrization_many_decorators_example[1-a]", @@ -97,7 +98,9 @@ def test_parametrization_many_decorators(executed_docstring_source): ) -def test_parametrization_decorators_with_partial_ids(executed_docstring_source): +def test_parametrization_decorators_with_partial_ids( + allure_pytest_runner: AllurePytestRunner +): """ >>> import pytest @@ -107,8 +110,10 @@ def test_parametrization_decorators_with_partial_ids(executed_docstring_source): ... pass """ + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, all_of( has_test_case( "test_two_marks_one_with_ids[1-A]", @@ -134,15 +139,18 @@ def test_parametrization_decorators_with_partial_ids(executed_docstring_source): ) -def test_dynamic_parameter_add(executed_docstring_source): +def test_dynamic_parameter_add(allure_pytest_runner: AllurePytestRunner): """ >>> import allure >>> def test_parameter_add(): ... allure.dynamic.parameter("param1", "param-value") """ + + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, has_test_case( "test_parameter_add", has_parameter("param1", "'param-value'") @@ -150,7 +158,7 @@ def test_dynamic_parameter_add(executed_docstring_source): ) -def test_dynamic_parameter_excluded(executed_docstring_source): +def test_dynamic_parameter_excluded(allure_pytest_runner: AllurePytestRunner): """ >>> import allure @@ -158,8 +166,10 @@ def test_dynamic_parameter_excluded(executed_docstring_source): ... allure.dynamic.parameter("param1", "param-value", excluded=True) """ + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, has_test_case( "test_parameter_excluded", has_parameter( @@ -171,7 +181,7 @@ def test_dynamic_parameter_excluded(executed_docstring_source): ) -def test_dynamic_parameter_mode(executed_docstring_source): +def test_dynamic_parameter_mode(allure_pytest_runner: AllurePytestRunner): """ >>> import allure @@ -179,8 +189,10 @@ def test_dynamic_parameter_mode(executed_docstring_source): ... allure.dynamic.parameter("param1", "param-value", mode=allure.parameter_mode.MASKED) """ + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, has_test_case( "test_parameter_mode", has_parameter( @@ -192,7 +204,7 @@ def test_dynamic_parameter_mode(executed_docstring_source): ) -def test_dynamic_parameter_override(executed_docstring_source): +def test_dynamic_parameter_override(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest ... import allure @@ -201,8 +213,11 @@ def test_dynamic_parameter_override(executed_docstring_source): ... def test_parameter_override(param1): ... allure.dynamic.parameter("param1", "readable-value") """ + + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, has_test_case( "test_parameter_override[param-id]", has_parameter("param1", "'readable-value'") @@ -210,7 +225,9 @@ def test_dynamic_parameter_override(executed_docstring_source): ) -def test_dynamic_parameter_override_from_fixture(executed_docstring_source): +def test_dynamic_parameter_override_from_fixture( + allure_pytest_runner: AllurePytestRunner +): """ >>> import pytest ... import allure @@ -224,8 +241,11 @@ def test_dynamic_parameter_override_from_fixture(executed_docstring_source): ... def test_parameter_override_from_fixture(fixt, param1): ... pass """ + + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, has_test_case( "test_parameter_override_from_fixture[param-id]", has_parameter("param1", "'readable-value'") @@ -233,7 +253,7 @@ def test_dynamic_parameter_override_from_fixture(executed_docstring_source): ) -def test_fullname_with_braces(executed_docstring_source): +def test_fullname_with_braces(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest ... import allure @@ -244,8 +264,10 @@ def test_fullname_with_braces(executed_docstring_source): ... pass """ + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, has_test_case( "test_with_braces[qwe][]", has_entry( diff --git a/tests/allure_pytest/acceptance/unicode_identifier/__init__.py b/tests/allure_pytest/acceptance/pytest_pluginmanager/__init__.py similarity index 100% rename from tests/allure_pytest/acceptance/unicode_identifier/__init__.py rename to tests/allure_pytest/acceptance/pytest_pluginmanager/__init__.py diff --git a/tests/allure_pytest/acceptance/pytest_pluginmanager/pytest_get_allure_plugin_test.py b/tests/allure_pytest/acceptance/pytest_pluginmanager/pytest_get_allure_plugin_test.py new file mode 100644 index 00000000..24d0cf81 --- /dev/null +++ b/tests/allure_pytest/acceptance/pytest_pluginmanager/pytest_get_allure_plugin_test.py @@ -0,0 +1,26 @@ +from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + +import allure +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status + + +@allure.feature("Integration") +def test_pytest_get_allure_listener_plugin( + allure_pytest_runner: AllurePytestRunner +): + """ + >>> def test_pytest_get_allure_listener_plugin(request): + ... assert request.config.pluginmanager.get_plugin('allure_listener') + """ + + output = allure_pytest_runner.run_docstring() + + assert_that( + output, + has_test_case( + "test_pytest_get_allure_listener_plugin", + with_status("passed") + ) + ) diff --git a/tests/allure_pytest/acceptance/status/base_call_status_test.py b/tests/allure_pytest/acceptance/status/base_call_status_test.py index a7f96364..81f3a338 100644 --- a/tests/allure_pytest/acceptance/status/base_call_status_test.py +++ b/tests/allure_pytest/acceptance/status/base_call_status_test.py @@ -1,4 +1,6 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.result import has_status_details @@ -6,52 +8,66 @@ from allure_commons_test.result import with_trace_contains -def test_passed(executed_docstring_source): +def test_passed(allure_pytest_runner: AllurePytestRunner): """ >>> def test_passed_example(): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_passed_example", - with_status("passed") - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_passed_example", + with_status("passed") + ) + ) -def test_failed(executed_docstring_source): +def test_failed(allure_pytest_runner: AllurePytestRunner): """ >>> def test_failed_example(): ... assert False """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_failed_example", - with_status("failed"), - has_status_details(with_message_contains("AssertionError"), - with_trace_contains("def test_failed_example():") - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_failed_example", + with_status("failed"), + has_status_details( + with_message_contains("AssertionError"), + with_trace_contains("def test_failed_example():") + ) + ) + ) -def test_broken(executed_docstring_source): +def test_broken(allure_pytest_runner: AllurePytestRunner): """ >>> def test_broken_example(): ... raise IndentationError() """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_broken_example", - with_status("broken"), - has_status_details(with_message_contains("IndentationError"), - with_trace_contains("def test_broken_example():") - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + assert_that( + allure_results, + has_test_case( + "test_broken_example", + with_status("broken"), + has_status_details( + with_message_contains("IndentationError"), + with_trace_contains("def test_broken_example():") + ) + ) + ) -def test_call_pytest_fail(executed_docstring_source): + +def test_call_pytest_fail(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -59,17 +75,22 @@ def test_call_pytest_fail(executed_docstring_source): ... pytest.fail() """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_call_pytest_fail_example", - with_status("failed"), - has_status_details(with_message_contains("Failed"), - with_trace_contains("def test_call_pytest_fail_example():") - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_call_pytest_fail_example", + with_status("failed"), + has_status_details( + with_message_contains("Failed"), + with_trace_contains("def test_call_pytest_fail_example():") + ) + ) + ) -def test_call_pytest_fail_with_reason(executed_docstring_source): +def test_call_pytest_fail_with_reason(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -77,12 +98,16 @@ def test_call_pytest_fail_with_reason(executed_docstring_source): ... pytest.fail("Fail message") """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_call_pytest_fail_with_reason_example", - with_status("failed"), - has_status_details(with_message_contains("Fail message"), - with_trace_contains("def test_call_pytest_fail_with_reason_example():") - ) - ) - - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_call_pytest_fail_with_reason_example", + with_status("failed"), + has_status_details( + with_message_contains("Fail message"), + with_trace_contains("def test_call_pytest_fail_with_reason_example():") + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/status/base_setup_status_test.py b/tests/allure_pytest/acceptance/status/base_setup_status_test.py index 9cec930c..cf2468bc 100644 --- a/tests/allure_pytest/acceptance/status/base_setup_status_test.py +++ b/tests/allure_pytest/acceptance/status/base_setup_status_test.py @@ -1,4 +1,6 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.result import has_status_details @@ -8,7 +10,7 @@ from allure_commons_test.container import has_before -def test_failed_fixture(executed_docstring_source): +def test_failed_fixture(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -20,25 +22,33 @@ def test_failed_fixture(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_failed_fixture_example", - with_status("failed"), - has_status_details(with_message_contains("AssertionError"), - with_trace_contains("def failed_fixture():") - ), - has_container(executed_docstring_source.allure_report, - has_before("failed_fixture", - with_status("failed"), - has_status_details(with_message_contains("AssertionError"), - with_trace_contains("failed_fixture") - ), - ), - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_failed_fixture_example", + with_status("failed"), + has_status_details( + with_message_contains("AssertionError"), + with_trace_contains("def failed_fixture():") + ), + has_container( + allure_results, + has_before( + "failed_fixture", + with_status("failed"), + has_status_details( + with_message_contains("AssertionError"), + with_trace_contains("failed_fixture") + ) ) + ) + ) + ) -def test_broken_fixture(executed_docstring_source): +def test_broken_fixture(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -50,25 +60,33 @@ def test_broken_fixture(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_broken_fixture_example", - with_status("broken"), - has_status_details(with_message_contains("IndexError"), - with_trace_contains("def broken_fixture():") - ), - has_container(executed_docstring_source.allure_report, - has_before("broken_fixture", - with_status("broken"), - has_status_details(with_message_contains("IndexError"), - with_trace_contains("broken_fixture") - ), - ), - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_broken_fixture_example", + with_status("broken"), + has_status_details( + with_message_contains("IndexError"), + with_trace_contains("def broken_fixture():") + ), + has_container( + allure_results, + has_before( + "broken_fixture", + with_status("broken"), + has_status_details( + with_message_contains("IndexError"), + with_trace_contains("broken_fixture") + ), ) + ) + ) + ) -def test_skip_fixture(executed_docstring_source): +def test_skip_fixture(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -80,24 +98,32 @@ def test_skip_fixture(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_skip_fixture_example", - with_status("skipped"), - has_status_details(with_message_contains("Skipped")), - has_container(executed_docstring_source.allure_report, - has_before("skip_fixture", - with_status("skipped"), - has_status_details( - with_message_contains("Skipped"), - with_trace_contains("skip_fixture") - ), - ), - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_skip_fixture_example", + with_status("skipped"), + has_status_details( + with_message_contains("Skipped") + ), + has_container( + allure_results, + has_before( + "skip_fixture", + with_status("skipped"), + has_status_details( + with_message_contains("Skipped"), + with_trace_contains("skip_fixture") + ) ) + ) + ) + ) -def test_pytest_fail_fixture(executed_docstring_source): +def test_pytest_fail_fixture(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -109,26 +135,33 @@ def test_pytest_fail_fixture(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_pytest_fail_fixture_example", - with_status("failed"), - has_status_details(with_message_contains("Failed"), - with_trace_contains("def pytest_fail_fixture():") - ), - has_container(executed_docstring_source.allure_report, - has_before("pytest_fail_fixture", - with_status("failed"), - has_status_details( - with_message_contains("Failed"), - with_trace_contains("pytest_fail_fixture") - ), - ), - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_pytest_fail_fixture_example", + with_status("failed"), + has_status_details( + with_message_contains("Failed"), + with_trace_contains("def pytest_fail_fixture():") + ), + has_container( + allure_results, + has_before( + "pytest_fail_fixture", + with_status("failed"), + has_status_details( + with_message_contains("Failed"), + with_trace_contains("pytest_fail_fixture") + ) ) + ) + ) + ) -def test_pytest_fail_with_reason_fixture(executed_docstring_source): +def test_pytest_fail_with_reason_fixture(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -140,20 +173,27 @@ def test_pytest_fail_with_reason_fixture(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_pytest_fail_with_reason_fixture_example", - with_status("failed"), - has_status_details(with_message_contains("Fail message"), - with_trace_contains("def pytest_fail_with_reason_fixture():") - ), - has_container(executed_docstring_source.allure_report, - has_before("pytest_fail_with_reason_fixture", - with_status("failed"), - has_status_details(with_message_contains("Fail message"), - with_trace_contains( - "pytest_fail_with_reason_fixture") - ), - ), - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_pytest_fail_with_reason_fixture_example", + with_status("failed"), + has_status_details( + with_message_contains("Fail message"), + with_trace_contains("def pytest_fail_with_reason_fixture():") + ), + has_container( + allure_results, + has_before( + "pytest_fail_with_reason_fixture", + with_status("failed"), + has_status_details( + with_message_contains("Fail message"), + with_trace_contains("pytest_fail_with_reason_fixture") + ) ) + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/status/base_step_status_test.py b/tests/allure_pytest/acceptance/status/base_step_status_test.py index 46aa6888..c5cb1a06 100644 --- a/tests/allure_pytest/acceptance/status/base_step_status_test.py +++ b/tests/allure_pytest/acceptance/status/base_step_status_test.py @@ -1,4 +1,6 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_step from allure_commons_test.result import with_status @@ -7,7 +9,7 @@ from allure_commons_test.result import with_trace_contains -def test_broken_step(executed_docstring_source): +def test_broken_step(allure_pytest_runner: AllurePytestRunner): """ >>> import allure @@ -16,23 +18,30 @@ def test_broken_step(executed_docstring_source): ... raise ZeroDivisionError """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_broken_step_example", - with_status("broken"), - has_status_details(with_message_contains("ZeroDivisionError"), - with_trace_contains("def test_broken_step_example():") - ), - has_step("Step", - with_status("broken"), - has_status_details(with_message_contains("ZeroDivisionError"), - with_trace_contains("test_broken_step_example") - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_broken_step_example", + with_status("broken"), + has_status_details( + with_message_contains("ZeroDivisionError"), + with_trace_contains("def test_broken_step_example():") + ), + has_step( + "Step", + with_status("broken"), + has_status_details( + with_message_contains("ZeroDivisionError"), + with_trace_contains("test_broken_step_example") ) + ) + ) + ) -def test_pytest_fail_in_step(executed_docstring_source): +def test_pytest_fail_in_step(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest >>> import allure @@ -42,23 +51,30 @@ def test_pytest_fail_in_step(executed_docstring_source): ... pytest.fail() """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_pytest_fail_in_step_example", - with_status("failed"), - has_status_details(with_message_contains("Failed"), - with_trace_contains("def test_pytest_fail_in_step_example():") - ), - has_step("Step", - with_status("failed"), - has_status_details(with_message_contains("Failed"), - with_trace_contains("test_pytest_fail_in_step_example") - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_pytest_fail_in_step_example", + with_status("failed"), + has_status_details( + with_message_contains("Failed"), + with_trace_contains("def test_pytest_fail_in_step_example():") + ), + has_step( + "Step", + with_status("failed"), + has_status_details( + with_message_contains("Failed"), + with_trace_contains("test_pytest_fail_in_step_example") ) + ) + ) + ) -def test_pytest_bytes_data_in_assert(executed_docstring_source): +def test_pytest_bytes_data_in_assert(allure_pytest_runner: AllurePytestRunner): """ >>> import allure @@ -67,18 +83,24 @@ def test_pytest_bytes_data_in_assert(executed_docstring_source): ... assert "0\\x82" == 1 """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_pytest_bytes_data_in_assert_example", - with_status("failed"), - has_status_details(with_message_contains("AssertionError: assert \'0\\x82\' == 1"), - with_trace_contains("def test_pytest_bytes_data_in_assert_example():") - ), - has_step("Step", - with_status("failed"), - has_status_details( - with_message_contains("AssertionError: assert \'0\\x82\' == 1"), - with_trace_contains("test_pytest_bytes_data_in_assert_example") - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_pytest_bytes_data_in_assert_example", + with_status("failed"), + has_status_details( + with_message_contains("AssertionError: assert \'0\\x82\' == 1"), + with_trace_contains("def test_pytest_bytes_data_in_assert_example():") + ), + has_step( + "Step", + with_status("failed"), + has_status_details( + with_message_contains("AssertionError: assert \'0\\x82\' == 1"), + with_trace_contains("test_pytest_bytes_data_in_assert_example") ) + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/status/base_teardown_status_test.py b/tests/allure_pytest/acceptance/status/base_teardown_status_test.py index 683ff1de..0194b458 100644 --- a/tests/allure_pytest/acceptance/status/base_teardown_status_test.py +++ b/tests/allure_pytest/acceptance/status/base_teardown_status_test.py @@ -1,4 +1,6 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.result import has_status_details @@ -8,7 +10,7 @@ from allure_commons_test.container import has_after -def test_failed_finalizer_fixture(executed_docstring_source): +def test_failed_finalizer_fixture(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -22,8 +24,10 @@ def test_failed_finalizer_fixture(executed_docstring_source): ... pass """ + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, has_test_case( "test_failed_finalizer_fixture_example", with_status("failed"), @@ -32,7 +36,7 @@ def test_failed_finalizer_fixture(executed_docstring_source): with_trace_contains("def fixture_finalizer():") ), has_container( - executed_docstring_source.allure_report, + allure_results, has_after( "failed_finalizer_fixture::fixture_finalizer", with_status("failed"), @@ -46,7 +50,7 @@ def test_failed_finalizer_fixture(executed_docstring_source): ) -def test_pytest_failed_finalizer_fixture(executed_docstring_source): +def test_pytest_failed_finalizer_fixture(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -60,8 +64,10 @@ def test_pytest_failed_finalizer_fixture(executed_docstring_source): ... pass """ + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, has_test_case( "test_pytest_failed_finalizer_fixture_example", with_status("failed"), @@ -70,7 +76,7 @@ def test_pytest_failed_finalizer_fixture(executed_docstring_source): with_trace_contains("def fixture_finalizer():") ), has_container( - executed_docstring_source.allure_report, + allure_results, has_after( "pytest_failed_finalizer_fixture::fixture_finalizer", with_status("failed"), diff --git a/tests/allure_pytest/acceptance/status/skip_call_status_test.py b/tests/allure_pytest/acceptance/status/skip_call_status_test.py index 70844f28..7138f17d 100644 --- a/tests/allure_pytest/acceptance/status/skip_call_status_test.py +++ b/tests/allure_pytest/acceptance/status/skip_call_status_test.py @@ -1,11 +1,13 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.result import has_status_details from allure_commons_test.result import with_message_contains -def test_skip(executed_docstring_source): +def test_skip(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -13,15 +15,21 @@ def test_skip(executed_docstring_source): ... pytest.skip() """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_skip_example", - with_status("skipped"), - has_status_details(with_message_contains("Skipped")) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_skip_example", + with_status("skipped"), + has_status_details( + with_message_contains("Skipped") + ) + ) + ) -def test_skip_with_reason(executed_docstring_source): +def test_skip_with_reason(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -29,15 +37,21 @@ def test_skip_with_reason(executed_docstring_source): ... pytest.skip("Skip reason") """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_skip_with_reason_example", - with_status("skipped"), - has_status_details(with_message_contains("Skipped: Skip reason")) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_skip_with_reason_example", + with_status("skipped"), + has_status_details( + with_message_contains("Skipped: Skip reason") + ) + ) + ) -def test_skip_decorator_and_reason(executed_docstring_source): +def test_skip_decorator_and_reason(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -46,15 +60,21 @@ def test_skip_decorator_and_reason(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_skip_decorator_and_reason_example", - with_status("skipped"), - has_status_details(with_message_contains("Skipped: Skip reason")) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + assert_that( + allure_results, + has_test_case( + "test_skip_decorator_and_reason_example", + with_status("skipped"), + has_status_details( + with_message_contains("Skipped: Skip reason") + ) + ) + ) -def test_skipif_true(executed_docstring_source): + +def test_skipif_true(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -63,15 +83,21 @@ def test_skipif_true(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_skipif_true_example", - with_status("skipped"), - has_status_details(with_message_contains("Skipped: Skip reason")) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_skipif_true_example", + with_status("skipped"), + has_status_details( + with_message_contains("Skipped: Skip reason") + ) + ) + ) -def test_skipif_false(executed_docstring_source): +def test_skipif_false(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -80,8 +106,12 @@ def test_skipif_false(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_skipif_false_example", - with_status("passed") - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_skipif_false_example", + with_status("passed") + ) + ) diff --git a/tests/allure_pytest/acceptance/status/skip_setup_status_test.py b/tests/allure_pytest/acceptance/status/skip_setup_status_test.py index 186bd601..436e8755 100644 --- a/tests/allure_pytest/acceptance/status/skip_setup_status_test.py +++ b/tests/allure_pytest/acceptance/status/skip_setup_status_test.py @@ -1,5 +1,7 @@ -import allure from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + +import allure from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.result import has_status_details @@ -10,7 +12,7 @@ @allure.feature("Fixture") -def test_skip_fixture(executed_docstring_source): +def test_skip_fixture(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -23,18 +25,26 @@ def test_skip_fixture(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_skip_fixture_example", - with_status("skipped"), - has_status_details(with_message_contains("Skipped")), - has_container(executed_docstring_source.allure_report, - has_before("skip_fixture", - with_status("skipped"), - has_status_details( - with_message_contains("Skipped"), - with_trace_contains("skip_fixture") - ), - ), - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_skip_fixture_example", + with_status("skipped"), + has_status_details( + with_message_contains("Skipped") + ), + has_container( + allure_results, + has_before( + "skip_fixture", + with_status("skipped"), + has_status_details( + with_message_contains("Skipped"), + with_trace_contains("skip_fixture") + ) ) + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/status/skip_step_status_test.py b/tests/allure_pytest/acceptance/status/skip_step_status_test.py index 637ed838..78dd10ea 100644 --- a/tests/allure_pytest/acceptance/status/skip_step_status_test.py +++ b/tests/allure_pytest/acceptance/status/skip_step_status_test.py @@ -1,4 +1,6 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_step from allure_commons_test.result import with_status @@ -7,7 +9,7 @@ from allure_commons_test.result import with_trace_contains -def test_skip_in_step(executed_docstring_source): +def test_skip_in_step(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest >>> import allure @@ -17,15 +19,23 @@ def test_skip_in_step(executed_docstring_source): ... pytest.skip() """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_skip_in_step_example", - with_status("skipped"), - has_status_details(with_message_contains("Skipped")), - has_step("Step", - with_status("skipped"), - has_status_details(with_message_contains("Skipped"), - with_trace_contains("test_skip_in_step") - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_skip_in_step_example", + with_status("skipped"), + has_status_details( + with_message_contains("Skipped") + ), + has_step( + "Step", + with_status("skipped"), + has_status_details( + with_message_contains("Skipped"), + with_trace_contains("test_skip_in_step") ) + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/status/skip_teardown_status_test.py b/tests/allure_pytest/acceptance/status/skip_teardown_status_test.py index 0ec758ce..531056c1 100644 --- a/tests/allure_pytest/acceptance/status/skip_teardown_status_test.py +++ b/tests/allure_pytest/acceptance/status/skip_teardown_status_test.py @@ -1,4 +1,6 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.result import has_status_details @@ -8,7 +10,7 @@ from allure_commons_test.container import has_after -def test_skip_finalizer_fixture(executed_docstring_source): +def test_skip_finalizer_fixture(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -22,13 +24,15 @@ def test_skip_finalizer_fixture(executed_docstring_source): ... pass """ + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, has_test_case( "test_skip_finalizer_fixture_example", with_status("passed"), has_container( - executed_docstring_source.allure_report, + allure_results, has_after( "skip_finalizer_fixture::fixture_finalizer", with_status("skipped"), diff --git a/tests/allure_pytest/acceptance/status/xfail_call_status_test.py b/tests/allure_pytest/acceptance/status/xfail_call_status_test.py index 5275723a..88b17689 100644 --- a/tests/allure_pytest/acceptance/status/xfail_call_status_test.py +++ b/tests/allure_pytest/acceptance/status/xfail_call_status_test.py @@ -1,4 +1,6 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.result import has_status_details @@ -6,7 +8,7 @@ from allure_commons_test.result import with_trace_contains -def test_xfail(executed_docstring_source): +def test_xfail(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -16,18 +18,25 @@ def test_xfail(executed_docstring_source): """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_xfail_example", - with_status("skipped"), - has_status_details(with_message_contains("XFAIL"), - with_message_contains("AssertionError"), - with_trace_contains("def test_xfail_example():") - ) - ) - ) - - -def test_xfail_with_reason_raise_mentioned_exception(executed_docstring_source): + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_xfail_example", + with_status("skipped"), + has_status_details( + with_message_contains("XFAIL"), + with_message_contains("AssertionError"), + with_trace_contains("def test_xfail_example():") + ) + ) + ) + + +def test_xfail_with_reason_raise_mentioned_exception( + allure_pytest_runner: AllurePytestRunner +): """ >>> import pytest @@ -37,19 +46,27 @@ def test_xfail_with_reason_raise_mentioned_exception(executed_docstring_source): """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_xfail_with_reason_raise_mentioned_exception_example", - with_status("skipped"), - has_status_details(with_message_contains("XFAIL Some reason"), - with_message_contains("AssertionError"), - with_trace_contains( - "def test_xfail_with_reason_raise_mentioned_exception_example():") - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_xfail_with_reason_raise_mentioned_exception_example", + with_status("skipped"), + has_status_details( + with_message_contains("XFAIL Some reason"), + with_message_contains("AssertionError"), + with_trace_contains( + "def test_xfail_with_reason_raise_mentioned_exception_example():" ) + ) + ) + ) -def test_xfail_raise_not_mentioned_exception(executed_docstring_source): +def test_xfail_raise_not_mentioned_exception( + allure_pytest_runner: AllurePytestRunner +): """ >>> import pytest @@ -58,18 +75,26 @@ def test_xfail_raise_not_mentioned_exception(executed_docstring_source): ... raise ZeroDivisionError """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_xfail_raise_not_mentioned_exception_example", - with_status("broken"), - has_status_details(with_message_contains("ZeroDivisionError"), - with_trace_contains( - "def test_xfail_raise_not_mentioned_exception_example():") - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_xfail_raise_not_mentioned_exception_example", + with_status("broken"), + has_status_details( + with_message_contains("ZeroDivisionError"), + with_trace_contains( + "def test_xfail_raise_not_mentioned_exception_example():" ) + ) + ) + ) -def test_xfail_do_not_raise_mentioned_exception(executed_docstring_source): +def test_xfail_do_not_raise_mentioned_exception( + allure_pytest_runner: AllurePytestRunner +): """ >>> import pytest @@ -78,16 +103,23 @@ def test_xfail_do_not_raise_mentioned_exception(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_xfail_do_not_raise_mentioned_exception_example", - with_status("passed"), - has_status_details(with_message_contains("XPASS"), - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_xfail_do_not_raise_mentioned_exception_example", + with_status("passed"), + has_status_details( + with_message_contains("XPASS"), + ) + ) + ) -def test_xfail_with_reason_do_not_raise_mentioned_exception(executed_docstring_source): +def test_xfail_with_reason_do_not_raise_mentioned_exception( + allure_pytest_runner: AllurePytestRunner +): """ >>> import pytest @@ -96,10 +128,15 @@ def test_xfail_with_reason_do_not_raise_mentioned_exception(executed_docstring_s ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_xfail_with_reason_do_not_raise_mentioned_exception_example", - with_status("passed"), - has_status_details(with_message_contains("XPASS Some reason"), - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_xfail_with_reason_do_not_raise_mentioned_exception_example", + with_status("passed"), + has_status_details( + with_message_contains("XPASS Some reason"), + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/status/xfail_setup_status_test.py b/tests/allure_pytest/acceptance/status/xfail_setup_status_test.py index 28e8e092..97006842 100644 --- a/tests/allure_pytest/acceptance/status/xfail_setup_status_test.py +++ b/tests/allure_pytest/acceptance/status/xfail_setup_status_test.py @@ -1,4 +1,6 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.result import has_status_details @@ -8,7 +10,7 @@ from allure_commons_test.container import has_before -def test_xfail_with_run_false(executed_docstring_source): +def test_xfail_with_run_false(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -17,15 +19,21 @@ def test_xfail_with_run_false(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_xfail_with_run_false_example", - with_status("skipped"), - has_status_details(with_message_contains("Failed: [NOTRUN]")), - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_xfail_with_run_false_example", + with_status("skipped"), + has_status_details( + with_message_contains("Failed: [NOTRUN]") + ) + ) + ) -def test_xfail_with_run_false_and_with_reason(executed_docstring_source): +def test_xfail_with_run_false_and_with_reason(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -34,15 +42,21 @@ def test_xfail_with_run_false_and_with_reason(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_xfail_with_run_false_and_with_reason_example", - with_status("skipped"), - has_status_details(with_message_contains("Failed: [NOTRUN] Some reason")) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + assert_that( + allure_results, + has_test_case( + "test_xfail_with_run_false_and_with_reason_example", + with_status("skipped"), + has_status_details( + with_message_contains("Failed: [NOTRUN] Some reason") + ) + ) + ) -def test_xfail_fixture(executed_docstring_source): + +def test_xfail_fixture(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -55,19 +69,27 @@ def test_xfail_fixture(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_xfail_fixture_example", - with_status("skipped"), - has_status_details(with_message_contains("NotImplementedError"), - with_trace_contains("def broken_fixture():") - ), - has_container(executed_docstring_source.allure_report, - has_before("broken_fixture", - with_status("broken"), - has_status_details(with_message_contains("NotImplementedError"), - with_trace_contains("broken_fixture") - ), - ), - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_xfail_fixture_example", + with_status("skipped"), + has_status_details( + with_message_contains("NotImplementedError"), + with_trace_contains("def broken_fixture():") + ), + has_container( + allure_results, + has_before( + "broken_fixture", + with_status("broken"), + has_status_details( + with_message_contains("NotImplementedError"), + with_trace_contains("broken_fixture") + ), + ), + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/status/xfail_step_status_test.py b/tests/allure_pytest/acceptance/status/xfail_step_status_test.py index 1e57bd4a..1ea66511 100644 --- a/tests/allure_pytest/acceptance/status/xfail_step_status_test.py +++ b/tests/allure_pytest/acceptance/status/xfail_step_status_test.py @@ -1,4 +1,6 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_step from allure_commons_test.result import with_status @@ -7,7 +9,7 @@ from allure_commons_test.result import with_trace_contains -def test_xfail_step_failure(executed_docstring_source): +def test_xfail_step_failure(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest >>> import allure @@ -18,17 +20,24 @@ def test_xfail_step_failure(executed_docstring_source): ... assert False """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_xfail_step_failure_example", - with_status("skipped"), - has_status_details(with_message_contains("AssertionError"), - with_trace_contains("def test_xfail_step_failure_example():") - ), - has_step("Step", - with_status("failed"), - has_status_details(with_message_contains("AssertionError"), - with_trace_contains("test_xfail_step_failure_example") - ) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_xfail_step_failure_example", + with_status("skipped"), + has_status_details( + with_message_contains("AssertionError"), + with_trace_contains("def test_xfail_step_failure_example():") + ), + has_step( + "Step", + with_status("failed"), + has_status_details( + with_message_contains("AssertionError"), + with_trace_contains("test_xfail_step_failure_example") ) + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/status/xfail_teardown_status_test.py b/tests/allure_pytest/acceptance/status/xfail_teardown_status_test.py index 0b295c06..23b3bf03 100644 --- a/tests/allure_pytest/acceptance/status/xfail_teardown_status_test.py +++ b/tests/allure_pytest/acceptance/status/xfail_teardown_status_test.py @@ -1,4 +1,6 @@ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.result import has_status_details @@ -8,7 +10,7 @@ from allure_commons_test.container import has_after -def test_xfail_failed_finalizer_fixture(executed_docstring_source): +def test_xfail_failed_finalizer_fixture(allure_pytest_runner: AllurePytestRunner): """ >>> import pytest @@ -23,8 +25,10 @@ def test_xfail_failed_finalizer_fixture(executed_docstring_source): ... pass """ + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, has_test_case( "test_xfail_failed_finalizer_fixture_example", with_status("passed"), @@ -32,7 +36,7 @@ def test_xfail_failed_finalizer_fixture(executed_docstring_source): with_message_contains("XPASS") ), has_container( - executed_docstring_source.allure_report, + allure_results, has_after( "failed_finalizer_fixture::fixture_finalizer", with_status("failed"), diff --git a/tests/allure_pytest/acceptance/step/outside_step_test.py b/tests/allure_pytest/acceptance/step/outside_step_test.py index a724628b..e6cb2e78 100644 --- a/tests/allure_pytest/acceptance/step/outside_step_test.py +++ b/tests/allure_pytest/acceptance/step/outside_step_test.py @@ -1,38 +1,45 @@ import allure from hamcrest import assert_that, not_ +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_step from allure_commons_test.container import has_container from allure_commons_test.container import has_before -def test_step_from_init_py(allured_testdir): - allured_testdir.testdir.makepyfile(__init__=""" +def test_step_from_init_py(allure_pytest_runner: AllurePytestRunner): + allure_pytest_runner.pytester.makepyfile(__init__=( + """ import allure @allure.step("function in __init__ marked as step") def step_from__init__(): pass - """) + """ + )) - allured_testdir.testdir.makepyfile(""" + allure_results = allure_pytest_runner.run_pytest( + """ from . import step_from__init__ def test_step_from_init_py_example(): step_from__init__() - """) - - allured_testdir.run_with_allure() + """ + ) - assert_that(allured_testdir.allure_report, - has_test_case("test_step_from_init_py_example", - has_step("function in __init__ marked as step") - ) - ) + assert_that( + allure_results, + has_test_case( + "test_step_from_init_py_example", + has_step("function in __init__ marked as step") + ) + ) -def test_fixture_with_step_from_conftest(allured_testdir): - allured_testdir.testdir.makeconftest(""" +def test_fixture_with_step_from_conftest(allure_pytest_runner: AllurePytestRunner): + conftest_content = ( + """ import allure import pytest @@ -44,28 +51,38 @@ def conftest_step(): @pytest.fixture def fixture_with_conftest_step(): conftest_step() - """) + """ + ) - allured_testdir.testdir.makepyfile(""" + testfile_content = ( + """ def test_fixture_with_step_from_conftest_example(fixture_with_conftest_step): pass - """) - - allured_testdir.run_with_allure() - - assert_that(allured_testdir.allure_report, - has_test_case("test_fixture_with_step_from_conftest_example", - has_container(allured_testdir.allure_report, - has_before("fixture_with_conftest_step", - has_step("step in conftest.py") - ) - ) - ) + """ + ) + + allure_results = allure_pytest_runner.run_pytest( + testfile_content, + conftest_literal=conftest_content + ) + + assert_that( + allure_results, + has_test_case( + "test_fixture_with_step_from_conftest_example", + has_container( + allure_results, + has_before( + "fixture_with_conftest_step", + has_step("step in conftest.py") ) + ) + ) + ) @allure.issue("232") -def test_call_decorated_as_step_function(executed_docstring_source): +def test_call_decorated_as_step_function(allure_pytest_runner: AllurePytestRunner): """ >>> import allure @@ -76,8 +93,14 @@ def test_call_decorated_as_step_function(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_call_decorated_as_step_function_example", - not_(has_step("step outside")) - ) - ) + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_call_decorated_as_step_function_example", + not_( + has_step("step outside") + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/step/step_parameters.py b/tests/allure_pytest/acceptance/step/step_parameters.py deleted file mode 100644 index 19e3bba8..00000000 --- a/tests/allure_pytest/acceptance/step/step_parameters.py +++ /dev/null @@ -1,57 +0,0 @@ -import pytest -from hamcrest import assert_that -from allure_commons.utils import represent -from allure_commons_test.report import has_test_case -from allure_commons_test.result import has_step -from allure_commons_test.result import has_parameter - - -def params_name(request): - node_id = request.node.nodeid - _, name = node_id.rstrip("]").split("[") - return name - - -@pytest.mark.parametrize( - ["args", "kwargs"], - [ - ([True], {"kwarg": False}), - ([True], {"kwarg": False}), - (["hi"], {"kwarg": None}), - ([None], {"kwarg": 42}) - ] -) -def test_step_parameters(executed_docstring_source, request, args, kwargs): - """ - >>> import pytest - >>> import allure - - >>> @allure.step - ... def step(arg, kwarg=None): - ... pass - - >>> @pytest.mark.parametrize( - ... ["args", "kwargs"], - ... [ - ... ([True], {"kwarg": False}), - ... ([True], {"kwarg": False}), - ... (["hi"], {"kwarg": None}), - ... ([None], {"kwarg": 42}) - ... ] - ... ) - ... def test_args_less_than_placeholders_example(args, kwargs): - ... step(*args, **kwargs) - """ - - test_name = f"test_args_less_than_placeholders_example[{params_name(request)}]" - - assert_that(executed_docstring_source.allure_report, - has_test_case(test_name, - has_step("step", - *([has_parameter("arg", represent(arg)) for arg in args] + - [has_parameter("kwarg", represent(kwarg)) for kwarg in kwargs.values()] - ) - - ) - ) - ) diff --git a/tests/allure_pytest/acceptance/step/step_parameters_test.py b/tests/allure_pytest/acceptance/step/step_parameters_test.py new file mode 100644 index 00000000..98c9b6b6 --- /dev/null +++ b/tests/allure_pytest/acceptance/step/step_parameters_test.py @@ -0,0 +1,42 @@ +from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + +from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_step +from allure_commons_test.result import has_parameter + + +def test_step_parameters(allure_pytest_runner: AllurePytestRunner): + """ + >>> import pytest + >>> import allure + + >>> @allure.step + ... def step(arg, kwarg=None): + ... pass + + >>> @pytest.mark.parametrize( + ... ["args", "kwargs"], + ... [ + ... ([True], {"kwarg": False}), + ... (["hi"], {"kwarg": None}), + ... ([None], {"kwarg": 42}) + ... ] + ... ) + ... def test_step_parameters(args, kwargs): + ... step(*args, **kwargs) + """ + + allure_results = allure_pytest_runner.run_docstring() + + assert_that( + allure_results, + has_test_case( + "test_step_parameters[args0-kwargs0]", + has_step( + "step", + has_parameter("arg", "True"), + has_parameter("kwarg", "False") + ) + ) + ) diff --git a/tests/allure_pytest/acceptance/step/step_placeholder_test.py b/tests/allure_pytest/acceptance/step/step_placeholder_test.py index 2ed86886..7f93b175 100644 --- a/tests/allure_pytest/acceptance/step/step_placeholder_test.py +++ b/tests/allure_pytest/acceptance/step/step_placeholder_test.py @@ -1,6 +1,8 @@ """ ./allure-pytest/examples/step/step_placeholder.rst """ -import pytest + from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_step from allure_commons_test.result import has_status_details @@ -8,9 +10,11 @@ from allure_commons_test.result import with_message_contains -def test_step_with_args_in_placeholder(executed_docstring_path): +def test_step_with_args_in_placeholder(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + assert_that( - executed_docstring_path.allure_report, + allure_results, has_test_case( "test_step_with_args_in_placeholder", has_step("Step with two args: 'first' and 'second'") @@ -18,9 +22,11 @@ def test_step_with_args_in_placeholder(executed_docstring_path): ) -def test_step_with_kwargs_in_placeholder(executed_docstring_path): +def test_step_with_kwargs_in_placeholder(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + assert_that( - executed_docstring_path.allure_report, + allure_results, has_test_case( "test_step_with_kwargs_in_placeholder", has_step("Step with two kwargs: '1' and 'second'") @@ -28,9 +34,11 @@ def test_step_with_kwargs_in_placeholder(executed_docstring_path): ) -def test_class_method_as_step(executed_docstring_path): +def test_class_method_as_step(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + assert_that( - executed_docstring_path.allure_report, + allure_results, has_test_case( "test_class_method_as_step", has_step("Class method step with 'first' and 'second'") @@ -38,7 +46,7 @@ def test_class_method_as_step(executed_docstring_path): ) -def test_args_less_than_placeholders(executed_docstring_source): +def test_args_less_than_placeholders(allure_pytest_runner: AllurePytestRunner): """ >>> import allure @@ -50,8 +58,10 @@ def test_args_less_than_placeholders(executed_docstring_source): ... step(0) """ + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, has_test_case( "test_args_less_than_placeholders_example", with_status("broken"), diff --git a/tests/allure_pytest/acceptance/step/step_test.py b/tests/allure_pytest/acceptance/step/step_test.py index f20f2fe6..9addf92c 100644 --- a/tests/allure_pytest/acceptance/step/step_test.py +++ b/tests/allure_pytest/acceptance/step/step_test.py @@ -1,42 +1,61 @@ """ ./allure-pytest/examples/step/step.rst """ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + from allure_commons_test.report import has_test_case from allure_commons_test.result import has_step -def test_inline_step(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_inline_step", - has_step("inline step") - ) - ) +def test_inline_step(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + assert_that( + allure_results, + has_test_case( + "test_inline_step", + has_step("inline step") + ) + ) -def test_reusable_step(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_reusable_step", - has_step("passed_step") - ) - ) +def test_reusable_step(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) -def test_nested_steps(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_nested_steps", - has_step("grand parent step", - has_step("parent step", - has_step("passed_step" - ) - ) - ) - ) - ) + assert_that( + allure_results, + has_test_case( + "test_reusable_step", + has_step("passed_step") + ) + ) -def test_class_method_as_step(executed_docstring_path): - assert_that(executed_docstring_path.allure_report, - has_test_case("test_class_method_as_step", - has_step("class method as step") - ) +def test_nested_steps(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_nested_steps", + has_step( + "grand parent step", + has_step( + "parent step", + has_step("passed_step") ) + ) + ) + ) + + +def test_class_method_as_step(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_class_method_as_step", + has_step("class method as step") + ) + ) diff --git a/tests/allure_pytest/acceptance/step/test_step_with_several_step_inside_thread.py b/tests/allure_pytest/acceptance/step/test_step_with_several_step_inside_thread.py index ff90f16d..88304eee 100644 --- a/tests/allure_pytest/acceptance/step/test_step_with_several_step_inside_thread.py +++ b/tests/allure_pytest/acceptance/step/test_step_with_several_step_inside_thread.py @@ -1,41 +1,45 @@ -from allure_commons_test.report import has_test_case -from allure_commons_test.result import has_step from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner +from allure_commons_test.report import has_test_case +from allure_commons_test.result import has_step -def test_step_with_thread(allured_testdir): - allured_testdir.testdir.makepyfile( - """ - from concurrent.futures import ThreadPoolExecutor - - import allure - @allure.step("thread {x}") - def parallel_step(x=1): - with allure.step("Sub-step in thread"): - pass +def test_step_with_thread(allure_pytest_runner: AllurePytestRunner): + """ + >>> from concurrent.futures import ThreadPoolExecutor + >>> import allure + >>> @allure.step("thread {x}") + ... def parallel_step(x=1): + ... with allure.step("Sub-step in thread"): + ... pass - def test_thread(): - with allure.step("Start in thread"): - with ThreadPoolExecutor(max_workers=2) as executor: - executor.map(parallel_step, [1, 2]) + >>> def test_thread(): + ... with allure.step("Start in thread"): + ... with ThreadPoolExecutor(max_workers=2) as executor: + ... executor.map(parallel_step, [1, 2]) """ - ) - allured_testdir.run_with_allure() + allure_results = allure_pytest_runner.run_docstring() - assert_that(allured_testdir.allure_report, - has_test_case("test_thread", - has_step("Start in thread", - has_step("thread 1", has_step("Sub-step in thread")), - has_step("thread 2") - ) - ) - ) + assert_that( + allure_results, + has_test_case( + "test_thread", + has_step( + "Start in thread", + has_step( + "thread 1", + has_step("Sub-step in thread") + ), + has_step("thread 2") + ) + ) + ) -def test_step_with_reused_threads(executed_docstring_source): +def test_step_with_reused_threads(allure_pytest_runner: AllurePytestRunner): """ >>> from concurrent.futures import ThreadPoolExecutor >>> from threading import Event @@ -63,8 +67,10 @@ def test_step_with_reused_threads(executed_docstring_source): ... __execute_randomly(executor) """ + allure_results = allure_pytest_runner.run_docstring() + assert_that( - executed_docstring_source.allure_report, + allure_results, has_test_case( "test_thread", has_step("thread 1"), diff --git a/tests/allure_pytest/acceptance/testplan/select_test_from_testplan_test.py b/tests/allure_pytest/acceptance/testplan/select_test_from_testplan_test.py index 19a69870..bb8679a4 100644 --- a/tests/allure_pytest/acceptance/testplan/select_test_from_testplan_test.py +++ b/tests/allure_pytest/acceptance/testplan/select_test_from_testplan_test.py @@ -1,71 +1,83 @@ import pytest -import json -import os -import inspect from hamcrest import assert_that, contains_inanyorder, ends_with -from tests.conftest import AlluredTestdir -from typing import Sequence +from tests.allure_pytest.pytest_runner import AllurePytestRunner @pytest.mark.parametrize( - ["planned_tests", "expected_tests"], + ["testplan", "expected_tests"], [ pytest.param( - [{"id": 1}, {"id": 2}], + {"tests": [{"id": 1}, {"id": 2}]}, ["test_number_one", "test_number_two"], id="ids-only" ), pytest.param( - [{"id": 1}, {"id": 3}, {"id": 4}], + {"tests": [{"id": 1}, {"id": 3}, {"id": 4}]}, ["test_number_one", "test_number_three"], id="id-for-test-with-two-ids" ), pytest.param( - [{"id": 1234}], + {"tests": [{"id": 1234}]}, [], id="id-nomatch" ), pytest.param( - [ - {"selector": "test_number_one"}, - {"selector": "test_number_three"} - ], + {"tests": [ + {"selector": "testplan_test#test_number_one"}, + {"selector": "testplan_test#test_number_three"} + ]}, ["test_number_one", "test_number_three"], id="selectors-only" ), pytest.param( - [{"selector": "test_without_number"}], + {"tests": [{"selector": "testplan_test#test_without_number"}]}, ["test_without_number"], id="selector-for-test-with-noid" ), pytest.param( - [{"id": 2, "selector": "test_number_two"}], + {"tests": [{"id": 2, "selector": "testplan_test#test_number_two"}]}, ["test_number_two"], id="id-selector-same-test" ), pytest.param( - [{"selector": "test_without_never"}], + {"tests": [{"selector": "testplan_test#test_without_never"}]}, [], id="selector-nomatch" ), pytest.param( - None, - ["test_number_one", "test_number_two", "test_number_three", "test_without_number"], - id="noplan" + {"tests": []}, + [ + "test_number_one", + "test_number_two", + "test_number_three", + "test_without_number" + ], + id="no-tests-in-plan" ), + + pytest.param( + {}, + [ + "test_number_one", + "test_number_two", + "test_number_three", + "test_without_number" + ], + id="empty-plan" + ) ] ) def test_select_by_testcase_id_test( - planned_tests: Sequence[dict], - expected_tests: Sequence[str], - allured_testdir: AlluredTestdir + allure_pytest_runner: AllurePytestRunner, + testplan, + expected_tests ): """ >>> import allure @@ -87,29 +99,18 @@ def test_select_by_testcase_id_test( ... pass """ - test_name = inspect.currentframe().f_code.co_name - if planned_tests: - for item in planned_tests: - if "selector" in item: - selector = item['selector'] - item["selector"] = f"{test_name}#{selector}" - - testplan = {"tests": planned_tests} - py_path = allured_testdir.testdir.makefile(".json", json.dumps(testplan)) - os.environ["ALLURE_TESTPLAN_PATH"] = str(py_path) - else: - os.environ.pop("ALLURE_TESTPLAN_PATH", None) - - allured_testdir.parse_docstring_source() - allured_testdir.run_with_allure("--rootdir", allured_testdir.testdir.path) + allure_results = allure_pytest_runner.run_docstring( + filename="testplan_test", + testplan=testplan + ) executed_full_names = [ - tc["fullName"] for tc in allured_testdir.allure_report.test_cases + tc["fullName"] for tc in allure_results.test_cases ] assert_that( executed_full_names, contains_inanyorder( - * [ ends_with(name) for name in expected_tests ] + * [ends_with(name) for name in expected_tests] ) ) diff --git a/tests/allure_pytest/acceptance/unicode_identifier/unicode_identifier_test.py b/tests/allure_pytest/acceptance/unicode_identifier/unicode_identifier_test.py deleted file mode 100644 index abce0b4f..00000000 --- a/tests/allure_pytest/acceptance/unicode_identifier/unicode_identifier_test.py +++ /dev/null @@ -1,37 +0,0 @@ -from hamcrest import assert_that -from allure_commons_test.report import has_test_case - - -def test_unicode_function_name(executed_docstring_source): - """ - >>> def test_unicode_func_déjà_vu(): - ... pass - """ - - assert_that(executed_docstring_source.allure_report, - has_test_case("test_unicode_func_déjà_vu") - ) - - -def test_unicode_method_name(executed_docstring_source): - """ - >>> class TestCase: - ... def test_unicode_method_例子(self): - ... pass - """ - - assert_that(executed_docstring_source.allure_report, - has_test_case("test_unicode_method_例子") - ) - - -def test_unicode_class_name(executed_docstring_source): - """ - >>> class TestCaseПервый: - ... def test_method(self): - ... pass - """ - - assert_that(executed_docstring_source.allure_report, - has_test_case("TestCaseПервый#test_method") - ) diff --git a/tests/allure_pytest/conftest.py b/tests/allure_pytest/conftest.py new file mode 100644 index 00000000..d179255b --- /dev/null +++ b/tests/allure_pytest/conftest.py @@ -0,0 +1,7 @@ +from pytest import fixture +from .pytest_runner import AllurePytestRunner + + +@fixture +def allure_pytest_runner(request, pytester): + yield AllurePytestRunner(request, pytester) diff --git a/tests/allure_pytest/external_packages_support/pytest_check/pytest_check_test.py b/tests/allure_pytest/external_packages_support/pytest_check/pytest_check_test.py deleted file mode 100644 index 9babf102..00000000 --- a/tests/allure_pytest/external_packages_support/pytest_check/pytest_check_test.py +++ /dev/null @@ -1,27 +0,0 @@ -import allure -from allure_commons_test.report import has_test_case -from allure_commons_test.result import with_status, with_message_contains, has_status_details -from hamcrest import assert_that - - -@allure.issue("376") -@allure.feature("Integration") -def test_pytest_check(allured_testdir): - """ - >>> import pytest_check as check - >>> def test_pytest_check_example(): - ... check.equal(1, 2, msg="First failure") - ... check.equal(1, 2, msg="Second failure") - """ - - allured_testdir.parse_docstring_source() - allured_testdir.run_with_allure() - - assert_that(allured_testdir.allure_report, - has_test_case("test_pytest_check_example", - with_status("failed"), - has_status_details(with_message_contains("First failure"), - with_message_contains("Second failure")) - ), - - ) diff --git a/tests/allure_pytest/external_packages_support/pytest_doctest/pytest_doctest_test.py b/tests/allure_pytest/external_packages_support/pytest_doctest/pytest_doctest_test.py deleted file mode 100644 index ce33fbd2..00000000 --- a/tests/allure_pytest/external_packages_support/pytest_doctest/pytest_doctest_test.py +++ /dev/null @@ -1,67 +0,0 @@ -import allure -from hamcrest import assert_that -from allure_commons_test.report import has_test_case -from allure_commons_test.result import with_status - - -@allure.feature("Integration") -def test_pytest_doctest(allured_testdir): - allured_testdir.testdir.makepyfile(''' - def some_func(): - """ - >>> some_func() - True - """ - return True - - ''') - - allured_testdir.run_with_allure("--doctest-modules") - - assert_that(allured_testdir.allure_report, - has_test_case("test_pytest_doctest.some_func", - with_status("passed")) - ) - - -@allure.feature("Integration") -def test_pytest_doctest_failed(allured_testdir): - allured_testdir.testdir.makepyfile(''' - def some_func(): - """ - >>> some_func() - True - """ - return not True - - ''') - - allured_testdir.run_with_allure("--doctest-modules") - - assert_that( - allured_testdir.allure_report, - has_test_case( - "test_pytest_doctest_failed.some_func", - with_status("failed") - ) - ) - - -@allure.feature("Integration") -def test_pytest_doctest_broken(allured_testdir): - allured_testdir.testdir.makepyfile(''' - def some_func(): - """ - >>> raise ValueError() - """ - ''') - - allured_testdir.run_with_allure("--doctest-modules") - - assert_that( - allured_testdir.allure_report, - has_test_case( - "test_pytest_doctest_broken.some_func", - with_status("broken") - ) - ) diff --git a/tests/allure_pytest/external_packages_support/pytest_flakes/pytest_flakes_test.py b/tests/allure_pytest/external_packages_support/pytest_flakes/pytest_flakes_test.py deleted file mode 100644 index 17a98d4a..00000000 --- a/tests/allure_pytest/external_packages_support/pytest_flakes/pytest_flakes_test.py +++ /dev/null @@ -1,30 +0,0 @@ -import allure -from hamcrest import assert_that -from allure_commons_test.report import has_test_case -from allure_commons_test.result import with_status - - -@allure.issue("352") -def test_pytest_flakes(allured_testdir): - """ - >>> from os.path import * - >>> def test_pytest_flakes_example(): - ... assert True - """ - - allured_testdir.parse_docstring_source() - allured_testdir.run_with_allure("--flakes") - - assert_that(allured_testdir.allure_report, - has_test_case("flake-8", - with_status("broken") - ), - - ) - - assert_that(allured_testdir.allure_report, - has_test_case("test_pytest_flakes_example", - with_status("passed") - ) - - ) diff --git a/tests/allure_pytest/external_packages_support/pytest_pluginmanager/pytest_get_allure_plugin_test.py b/tests/allure_pytest/external_packages_support/pytest_pluginmanager/pytest_get_allure_plugin_test.py deleted file mode 100644 index 77d0a735..00000000 --- a/tests/allure_pytest/external_packages_support/pytest_pluginmanager/pytest_get_allure_plugin_test.py +++ /dev/null @@ -1,19 +0,0 @@ -import allure -from hamcrest import assert_that -from allure_commons_test.report import has_test_case -from allure_commons_test.result import with_status - - -@allure.feature("Integration") -def test_pytest_get_allure_listener_plugin(allured_testdir): - allured_testdir.testdir.makepyfile(""" - def test_pytest_get_allure_listener_plugin(request): - assert request.config.pluginmanager.get_plugin('allure_listener') - """) - - allured_testdir.run_with_allure() - - assert_that(allured_testdir.allure_report, - has_test_case("test_pytest_get_allure_listener_plugin", - with_status("passed")) - ) diff --git a/tests/allure_pytest/external_packages_support/pytest_rerunfailures/pytest_rerunfailures_test.py b/tests/allure_pytest/external_packages_support/pytest_rerunfailures/pytest_rerunfailures_test.py deleted file mode 100644 index 2f9aea70..00000000 --- a/tests/allure_pytest/external_packages_support/pytest_rerunfailures/pytest_rerunfailures_test.py +++ /dev/null @@ -1,31 +0,0 @@ -import allure -import pytest -from hamcrest import assert_that -from allure_commons_test.report import has_test_case -from allure_commons_test.result import with_status - - -@allure.issue("140") -@allure.feature("Integration") -@pytest.mark.parametrize("countdown", [2, 4]) -def test_pytest_rerunfailures(allured_testdir, countdown): - allured_testdir.testdir.makepyfile(f""" - import threading - import pytest - - back_to_normal = threading.local() - - @pytest.mark.flaky(reruns={countdown}) - def test_pytest_rerunfailures_example(): - countdown = getattr(back_to_normal, "countdown", 3) - back_to_normal.countdown = countdown - 1 - assert not countdown > 0 - - """) - - allured_testdir.run_with_allure() - - assert_that(allured_testdir.allure_report, - has_test_case("test_pytest_rerunfailures_example", - with_status("failed" if countdown == 2 else "passed")) - ) diff --git a/tests/allure_pytest/external_packages_support/pytest_xdist/__init__.py b/tests/allure_pytest/external_packages_support/pytest_xdist/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/allure_pytest/external_packages_support/pytest_xdist/pytest_xdist_select_test.py b/tests/allure_pytest/external_packages_support/pytest_xdist/pytest_xdist_select_test.py deleted file mode 100644 index 448eed81..00000000 --- a/tests/allure_pytest/external_packages_support/pytest_xdist/pytest_xdist_select_test.py +++ /dev/null @@ -1,32 +0,0 @@ -import allure -import pytest -from hamcrest import assert_that, ends_with, has_entry -from allure_commons_test.report import has_only_testcases - - -@allure.issue("292") -@allure.feature("Integration") -@pytest.mark.real_logger -def test_xdist_and_select_test_by_bdd_label(allured_testdir): - """ - >>> import pytest - >>> import allure - - >>> @pytest.mark.foo - ... def test_with_mark_foo(): - ... print ("hello") - - >>> @allure.feature("boo") - ... def test_with_feature_boo(): - ... print ("hello") - """ - - allured_testdir.parse_docstring_source() - allured_testdir.run_with_allure("-v", "--allure-features=boo", "-n1") - - assert_that(allured_testdir.allure_report, - has_only_testcases( - has_entry("fullName", - ends_with("test_with_feature_boo") - ) - )) diff --git a/tests/allure_pytest/external_packages_support/__init__.py b/tests/allure_pytest/externals/__init__.py similarity index 100% rename from tests/allure_pytest/external_packages_support/__init__.py rename to tests/allure_pytest/externals/__init__.py diff --git a/tests/allure_pytest/external_packages_support/pytest_check/__init__.py b/tests/allure_pytest/externals/pytest_check/__init__.py similarity index 100% rename from tests/allure_pytest/external_packages_support/pytest_check/__init__.py rename to tests/allure_pytest/externals/pytest_check/__init__.py diff --git a/tests/allure_pytest/externals/pytest_check/pytest_check_test.py b/tests/allure_pytest/externals/pytest_check/pytest_check_test.py new file mode 100644 index 00000000..d569fb5f --- /dev/null +++ b/tests/allure_pytest/externals/pytest_check/pytest_check_test.py @@ -0,0 +1,38 @@ +import pytest +from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + +import allure +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status, with_message_contains, has_status_details + + +@pytest.fixture +def check_runner(allure_pytest_runner: AllurePytestRunner): + allure_pytest_runner.enable_plugins("check") + yield allure_pytest_runner + + +@allure.issue("376") +@allure.feature("Integration") +def test_pytest_check(check_runner: AllurePytestRunner): + """ + >>> import pytest_check as check + >>> def test_pytest_check_example(): + ... check.equal(1, 2, msg="First failure") + ... check.equal(1, 2, msg="Second failure") + """ + + output = check_runner.run_docstring() + + assert_that( + output, + has_test_case( + "test_pytest_check_example", + with_status("failed"), + has_status_details( + with_message_contains("First failure"), + with_message_contains("Second failure") + ) + ) + ) diff --git a/tests/allure_pytest/external_packages_support/pytest_doctest/__init__.py b/tests/allure_pytest/externals/pytest_doctest/__init__.py similarity index 100% rename from tests/allure_pytest/external_packages_support/pytest_doctest/__init__.py rename to tests/allure_pytest/externals/pytest_doctest/__init__.py diff --git a/tests/allure_pytest/externals/pytest_doctest/pytest_doctest_test.py b/tests/allure_pytest/externals/pytest_doctest/pytest_doctest_test.py new file mode 100644 index 00000000..92945cba --- /dev/null +++ b/tests/allure_pytest/externals/pytest_doctest/pytest_doctest_test.py @@ -0,0 +1,63 @@ +from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + +import allure +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status + + +@allure.feature("Integration") +def test_pytest_doctest(allure_pytest_runner: AllurePytestRunner): + """ + >>> def some_func(): + ... ''' + ... >>> some_func() + ... True + ... ''' + ... return True + + """ + + output = allure_pytest_runner.run_docstring("--doctest-modules") + + assert_that(output, has_test_case( + "test_pytest_doctest.some_func", + with_status("passed") + )) + + +@allure.feature("Integration") +def test_pytest_doctest_failed(allure_pytest_runner: AllurePytestRunner): + """ + >>> def some_func(): + ... ''' + ... >>> some_func() + ... True + ... ''' + ... return not True + + """ + + output = allure_pytest_runner.run_docstring("--doctest-modules") + + assert_that(output, has_test_case( + "test_pytest_doctest_failed.some_func", + with_status("failed") + )) + + +@allure.feature("Integration") +def test_pytest_doctest_broken(allure_pytest_runner: AllurePytestRunner): + """ + >>> def some_func(): + ... ''' + ... >>> raise ValueError() + ... ''' + """ + + output = allure_pytest_runner.run_docstring("--doctest-modules") + + assert_that(output, has_test_case( + "test_pytest_doctest_broken.some_func", + with_status("broken") + )) diff --git a/tests/allure_pytest/external_packages_support/pytest_flakes/__init__.py b/tests/allure_pytest/externals/pytest_flakes/__init__.py similarity index 100% rename from tests/allure_pytest/external_packages_support/pytest_flakes/__init__.py rename to tests/allure_pytest/externals/pytest_flakes/__init__.py diff --git a/tests/allure_pytest/externals/pytest_flakes/pytest_flakes_test.py b/tests/allure_pytest/externals/pytest_flakes/pytest_flakes_test.py new file mode 100644 index 00000000..7358b660 --- /dev/null +++ b/tests/allure_pytest/externals/pytest_flakes/pytest_flakes_test.py @@ -0,0 +1,35 @@ +import pytest +from hamcrest import assert_that, all_of +from tests.allure_pytest.pytest_runner import AllurePytestRunner + +import allure +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status + + +@pytest.fixture +def flakes_runner(allure_pytest_runner: AllurePytestRunner): + allure_pytest_runner.enable_plugins("flakes") + yield allure_pytest_runner + + +@allure.issue("352") +def test_pytest_flakes(flakes_runner: AllurePytestRunner): + """ + >>> from os.path import * + >>> def test_pytest_flakes_example(): + ... assert True + """ + + output = flakes_runner.run_docstring("--flakes") + + assert_that(output, all_of( + has_test_case( + "flake-8", + with_status("broken") + ), + has_test_case( + "test_pytest_flakes_example", + with_status("passed") + ) + )) diff --git a/tests/allure_pytest/external_packages_support/pytest_lazy_fixture/__init__.py b/tests/allure_pytest/externals/pytest_lazy_fixture/__init__.py similarity index 100% rename from tests/allure_pytest/external_packages_support/pytest_lazy_fixture/__init__.py rename to tests/allure_pytest/externals/pytest_lazy_fixture/__init__.py diff --git a/tests/allure_pytest/external_packages_support/pytest_lazy_fixture/pytest_lazy_fixture_test.py b/tests/allure_pytest/externals/pytest_lazy_fixture/pytest_lazy_fixture_test.py similarity index 50% rename from tests/allure_pytest/external_packages_support/pytest_lazy_fixture/pytest_lazy_fixture_test.py rename to tests/allure_pytest/externals/pytest_lazy_fixture/pytest_lazy_fixture_test.py index 809107a2..cdb3bb08 100644 --- a/tests/allure_pytest/external_packages_support/pytest_lazy_fixture/pytest_lazy_fixture_test.py +++ b/tests/allure_pytest/externals/pytest_lazy_fixture/pytest_lazy_fixture_test.py @@ -1,12 +1,21 @@ -import allure +import pytest from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + +import allure from allure_commons_test.report import has_test_case from allure_commons_test.container import has_container from allure_commons_test.container import has_before +@pytest.fixture +def lazy_fixture_runner(allure_pytest_runner: AllurePytestRunner): + allure_pytest_runner.enable_plugins("lazy-fixture") + yield allure_pytest_runner + + @allure.feature("Integration") -def test_lazy_fixture(executed_docstring_source): +def test_lazy_fixture(lazy_fixture_runner: AllurePytestRunner): """ >>> import pytest ... from pytest_lazyfixture import lazy_fixture @@ -20,17 +29,22 @@ def test_lazy_fixture(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_lazy_fixture_example", - has_container(executed_docstring_source.allure_report, - has_before("my_lazy_fixture") - ), - ) - ) + output = lazy_fixture_runner.run_docstring() + + assert_that( + output, + has_test_case( + "test_lazy_fixture_example", + has_container( + output, + has_before("my_lazy_fixture") + ) + ) + ) @allure.feature("Integration") -def test_nested_lazy_fixture(executed_docstring_source): +def test_nested_lazy_fixture(lazy_fixture_runner: AllurePytestRunner): """ >>> import pytest ... from pytest_lazyfixture import lazy_fixture @@ -47,10 +61,15 @@ def test_nested_lazy_fixture(executed_docstring_source): ... pass """ - assert_that(executed_docstring_source.allure_report, - has_test_case("test_nested_lazy_fixture_example", - has_container(executed_docstring_source.allure_report, - has_before("my_lazy_fixture") - ), - ) - ) + output = lazy_fixture_runner.run_docstring() + + assert_that( + output, + has_test_case( + "test_nested_lazy_fixture_example", + has_container( + output, + has_before("my_lazy_fixture") + ) + ) + ) diff --git a/tests/allure_pytest/external_packages_support/pytest_pluginmanager/__init__.py b/tests/allure_pytest/externals/pytest_rerunfailures/__init__.py similarity index 100% rename from tests/allure_pytest/external_packages_support/pytest_pluginmanager/__init__.py rename to tests/allure_pytest/externals/pytest_rerunfailures/__init__.py diff --git a/tests/allure_pytest/externals/pytest_rerunfailures/pytest_rerunfailures_test.py b/tests/allure_pytest/externals/pytest_rerunfailures/pytest_rerunfailures_test.py new file mode 100644 index 00000000..f2611788 --- /dev/null +++ b/tests/allure_pytest/externals/pytest_rerunfailures/pytest_rerunfailures_test.py @@ -0,0 +1,43 @@ +import pytest +from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + +import allure +from allure_commons_test.report import has_test_case +from allure_commons_test.result import with_status + + +@pytest.fixture +def rerunfailures_runner(allure_pytest_runner: AllurePytestRunner): + allure_pytest_runner.enable_plugins("rerunfailures") + yield allure_pytest_runner + + +@allure.issue("140") +@allure.feature("Integration") +@pytest.mark.parametrize("countdown, status", [(2, "failed"), (4, "passed")]) +def test_pytest_rerunfailures( + rerunfailures_runner: AllurePytestRunner, + countdown, + status +): + testfile_content = ( + f""" + import pytest + + @pytest.mark.flaky(reruns={countdown}) + def test_pytest_rerunfailures_example(request): + run = request.node.execution_count + assert run == 4 + """ + ) + + output = rerunfailures_runner.run_pytest(testfile_content) + + assert_that( + output, + has_test_case( + "test_pytest_rerunfailures_example", + with_status(status) + ) + ) diff --git a/tests/allure_pytest/external_packages_support/pytest_rerunfailures/__init__.py b/tests/allure_pytest/externals/pytest_xdist/__init__.py similarity index 100% rename from tests/allure_pytest/external_packages_support/pytest_rerunfailures/__init__.py rename to tests/allure_pytest/externals/pytest_xdist/__init__.py diff --git a/tests/allure_pytest/externals/pytest_xdist/pytest_xdist_select_test.py b/tests/allure_pytest/externals/pytest_xdist/pytest_xdist_select_test.py new file mode 100644 index 00000000..d9984fc1 --- /dev/null +++ b/tests/allure_pytest/externals/pytest_xdist/pytest_xdist_select_test.py @@ -0,0 +1,39 @@ +import pytest +from hamcrest import assert_that, ends_with, has_entry +from tests.allure_pytest.pytest_runner import AllurePytestRunner + +import allure +from allure_commons_test.report import has_only_testcases + + +@pytest.fixture +def xdist_runner(allure_pytest_runner: AllurePytestRunner): + allure_pytest_runner.in_memory = False + allure_pytest_runner.enable_plugins("xdist") + yield allure_pytest_runner + + +@allure.issue("292") +@allure.feature("Integration") +def test_xdist_and_select_test_by_bdd_label(xdist_runner: AllurePytestRunner): + """ + >>> import pytest + >>> import allure + + >>> @pytest.mark.foo + ... def test_with_mark_foo(): + ... print ("hello") + + >>> @allure.feature("boo") + ... def test_with_feature_boo(): + ... print ("hello") + """ + + output = xdist_runner.run_docstring("-v", "--allure-features=boo", "-n1") + + assert_that(output, has_only_testcases( + has_entry( + "fullName", + ends_with("test_with_feature_boo") + ) + )) diff --git a/tests/allure_pytest/pytest_runner.py b/tests/allure_pytest/pytest_runner.py new file mode 100644 index 00000000..6637d519 --- /dev/null +++ b/tests/allure_pytest/pytest_runner.py @@ -0,0 +1,206 @@ +from doctest import script_from_examples +from pathlib import Path +from pytest import FixtureRequest, Pytester, StashKey +from tests.e2e import AllureFrameworkRunner, altered_env +from typing import Sequence, Tuple + +from allure_commons.logger import AllureMemoryLogger + + +class AllurePytestRunner(AllureFrameworkRunner): + """A runner for allure_pytest integration. + + Note: + Automatic plugin loading is disabled for all test runs initiated using + an instance of this class. If you need to test allure compatibility with + some pytest plugin, use :method:`AllurePytestRunner` or + :method:`AllurePytestRunner` to explicitly request its loading. + + """ + + DOCTEST_RESULT_KEY = StashKey() + + def __init__(self, request: FixtureRequest, pytester: Pytester): + super().__init__(request, pytester) + self.select_plugins("allure_pytest") + + def enable_plugins(self, *plugins: str) -> None: + """Request loading of pytest plugins on subsequent runs. Those plugins + are added into the list of the previously requested ones. + + """ + + self.enabled_plugins.update(plugins) + + def select_plugins(self, *plugins: str) -> None: + """Request loading of pytest plugins on subsequent runs. All other + plugins will not be loaded. + + Note: + Don't forget to add an allure integration plugin as well (i.e., + allure_pytest). + + """ + + self.enabled_plugins = set(plugins) + + def run_docstring( + self, + *cli_args: str, + filename: str | Path = None, + testplan: dict = None + ) -> AllureMemoryLogger: + """Runs a doctest from a docstring of the current test node (or one of its + parents). + + Arguments: + *cli_args (str): pytest CLI arguments + + Keyword Arguments: + filename (str | Path): an optional file name where the doctest is + saved before pytest is run. This should be a relative name. + testplan (dict): an optional allure testplan data. + + Returns: + allure_commons.logger.AllureMemoryLogger: allure results that were + collected during the test run. + """ + docstring = self._find_docstring() + testfile_content = script_from_examples(docstring) + return self.run_pytest( + (filename, testfile_content), + cli_args=cli_args, + testplan=testplan + ) + + def run_docpath_examples( + self, + *cli_args: str, + filename: str | Path = None, + testplan: dict = None, + cache: bool = False + ) -> AllureMemoryLogger: + """Runs a doctest from a document denoted by a path in docstring of + the current test node (or one of its parents). + + If the path is relative, it is prepended with the rootdir. + + Arguments: + *cli_args (str): pytest CLI arguments. + + Keyword Arguments: + filename (str | Path): an optional file name where the doctest is + saved before pytest is run. This should be a relative name. + testplan (dict): an optional allure testplan data. + cache (bool): set this flag if the allure results should be cached + on a node which docstring was originally used as the path. This + improves the performance if you have multiple tests on the same + document. Be careful though, this may lead to unexpected results + if the tests are relied upon different environments (i.e., the + CLI arguments are different). Only one run can be cached for + any given test. + + Returns: + allure_commons.logger.AllureMemoryLogger: allure results that were + collected during the test run. + """ + + if cache: + return self._cache_docstring_test_result( + AllurePytestRunner.DOCTEST_RESULT_KEY, + self.__test_docpath_examples, + filename, + cli_args, + testplan + ) + return self.__test_docpath_examples( + filename=filename, + cli_args=cli_args, + testplan=testplan, + docpath=self._find_docstring() + ) + + def run_pytest( + self, + *testfile_literals: str | Tuple[str | Path, str], + conftest_literal: str = None, + cli_args: Sequence[str] = None, + testplan: dict = None + ) -> AllureMemoryLogger: + """Runs a nested pytest session in an isolated allure context. + + Arguments: + *testfile_literals (str | Tuple[str | Path, str]): test files to + run. Each test file is represented either as a content string or + as a tuple of a path and a string. The path should be relative + to the pytester's path. + + Keyword arguments: + conftest_literal (str): an optional conftest.py content. + cli_args (Sequence[str]): pytest CLI arguments. + testplan (dict): an optional allure testplan data. + + Returns: + allure_commons.logger.AllureMemoryLogger: allure results that were + collected during the test run. + """ + + if conftest_literal: + self.pytester.makeconftest(conftest_literal) + if cli_args is None: + cli_args = () + plugin_args = self.__iter_plugin_args() + pytest_args = [ + "--alluredir", + self.pytester.path, + *plugin_args, + *cli_args + ] + self.__generate_testfiles(testfile_literals) + return self._run( + pytest_args, + testplan_content=testplan + ) + + def _run_framework(self, options): + with altered_env(PYTEST_DISABLE_PLUGIN_AUTOLOAD="true"): + self.pytester.runpytest(*options) + + def __generate_testfiles(self, testfiles): + for testfile in testfiles: + filepath = None + content = testfile + if not isinstance(testfile, str): + filepath, content = testfile + if filepath: + extension = Path(filepath).suffix or ".py" + self.pytester.makefile( + extension, + **{str(filepath): content} + ) + else: + self.pytester.makepyfile(content) + + def __test_docpath_examples( + self, + filename, + cli_args, + testplan, + docpath, + ): + docpath = docpath.strip() + doc_content = self._read_file(docpath, self.request.config.rootpath) + testfile_content = script_from_examples(doc_content) + return self.run_pytest( + (filename, testfile_content), + cli_args=cli_args, + testplan=testplan + ) + + def __iter_plugin_args(self): + for plugin_package in self.enabled_plugins: + yield "-p" + yield plugin_package + + +__all__ = ["AllurePytestRunner"] diff --git a/tests/allure_pytest_bdd/acceptance/scenario_outline_test.py b/tests/allure_pytest_bdd/acceptance/scenario_outline_test.py index c194fb19..ccf20611 100644 --- a/tests/allure_pytest_bdd/acceptance/scenario_outline_test.py +++ b/tests/allure_pytest_bdd/acceptance/scenario_outline_test.py @@ -1,15 +1,58 @@ """ ./allure-pytest-bdd/examples/scenario-outline """ from hamcrest import assert_that, all_of +from tests.allure_pytest.pytest_runner import AllurePytestRunner from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.result import has_step from allure_commons_test.result import has_parameter -def test_scenario_outline(executed_docstring_directory): +def test_scenario_outline(allure_pytest_bdd_runner: AllurePytestRunner): + feature_content = ( + """ + Feature: Allure report for scenario outline + Scenario Outline: Two examples with two parameters each + Given first step for value + When something is done with the value + Then check postconditions using and + + Examples: + | first | second | + | Alpha | 1 | + | Bravo | 2 | + """ + ) + steps_content = ( + """ + from pytest_bdd import scenario, given, when, then + from pytest_bdd import parsers + + @scenario("outline.feature", "Two examples with two parameters each") + def test_scenario_outline(): + pass + + @given(parsers.parse("first step for {first} value")) + def given_first_step_for_first_value(first): + pass + + @when(parsers.parse("something is done with the value {second}")) + def when_something_is_done_with_the_value_second(second): + pass + + @then(parsers.parse("check postconditions using {first} and {second}")) + def then_check_postconditions_using_first_and_second(first, second): + pass + """ + ) + + output = allure_pytest_bdd_runner.run_pytest( + ("outline.feature", feature_content), + steps_content, + ) + assert_that( - executed_docstring_directory.allure_report, + output, all_of( has_test_case( "Two examples with two parameters each", @@ -30,4 +73,4 @@ def test_scenario_outline(executed_docstring_directory): has_parameter("second", "2") ) ) - ) \ No newline at end of file + ) diff --git a/tests/allure_pytest_bdd/acceptance/scenario_test.py b/tests/allure_pytest_bdd/acceptance/scenario_test.py index 14e93afa..a381f088 100644 --- a/tests/allure_pytest_bdd/acceptance/scenario_test.py +++ b/tests/allure_pytest_bdd/acceptance/scenario_test.py @@ -1,15 +1,52 @@ """ ./allure-pytest-bdd/examples/simple-scenario """ from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.result import has_step from allure_commons_test.result import has_history_id -def test_simple_passed_scenario(executed_docstring_directory): +def test_simple_passed_scenario(allure_pytest_bdd_runner: AllurePytestRunner): + feature_content = ( + """ + Feature: Basic allure-pytest-bdd usage + Scenario: Simple passed example + Given the preconditions are satisfied + When the action is invoked + Then the postconditions are held + """ + ) + steps_content = ( + """ + from pytest_bdd import scenario, given, when, then + + @scenario("scenario.feature", "Simple passed example") + def test_scenario_passes(): + pass + + @given("the preconditions are satisfied") + def given_the_preconditions_are_satisfied(): + pass + + @when("the action is invoked") + def when_the_action_is_invoked(): + pass + + @then("the postconditions are held") + def then_the_postconditions_are_held(): + pass + """ + ) + + output = allure_pytest_bdd_runner.run_pytest( + ("scenario.feature", feature_content), + steps_content + ) + assert_that( - executed_docstring_directory.allure_report, + output, has_test_case( "Simple passed example", with_status("passed"), @@ -18,4 +55,4 @@ def test_simple_passed_scenario(executed_docstring_directory): has_step("Then the postconditions are held"), has_history_id() ) - ) \ No newline at end of file + ) diff --git a/tests/allure_pytest_bdd/conftest.py b/tests/allure_pytest_bdd/conftest.py index 13f8f55a..2252e93b 100644 --- a/tests/allure_pytest_bdd/conftest.py +++ b/tests/allure_pytest_bdd/conftest.py @@ -1,7 +1,9 @@ import pytest -from tests.conftest import AlluredTestdir +from tests.allure_pytest.pytest_runner import AllurePytestRunner + @pytest.fixture -def allured_testdir(allured_testdir: AlluredTestdir): - allured_testdir.select_plugins("allure_pytest_bdd") - return allured_testdir \ No newline at end of file +def allure_pytest_bdd_runner(request, pytester): + runner = AllurePytestRunner(request, pytester) + runner.select_plugins("pytest-bdd", "allure_pytest_bdd") + yield runner diff --git a/tests/allure_robotframework/acceptance/allure_api/attachment/attachment_test.py b/tests/allure_robotframework/acceptance/allure_api/attachment/attachment_test.py index 505a7b1e..7f016402 100644 --- a/tests/allure_robotframework/acceptance/allure_api/attachment/attachment_test.py +++ b/tests/allure_robotframework/acceptance/allure_api/attachment/attachment_test.py @@ -1,12 +1,14 @@ """ ./allure-robotframework/examples/attachment.rst """ +from pytest import MonkeyPatch from hamcrest import assert_that, equal_to -from tests.allure_robotframework.conftest import AllureRobotRunner +from tests.allure_robotframework.robot_runner import AllureRobotRunner from allure_commons_test.report import has_test_case from allure_commons_test.result import has_attachment_with_content from allure_commons_test.result import has_step from allure_commons_test.result import with_status + def test_data_attachment_with_default_name_and_type( robot_runner: AllureRobotRunner ): @@ -105,8 +107,11 @@ def test_file_attachment_with_name_and_type( ) -def test_autoattach_wrapper(robot_runner: AllureRobotRunner): - robot_runner.monkeypatch.syspath_prepend(".") +def test_autoattach_wrapper( + robot_runner: AllureRobotRunner, + monkeypatch: MonkeyPatch +): + monkeypatch.syspath_prepend(".") robot_runner.run_robotframework( suite_rst_ids={"selenium-wrapper.robot": "selenium-suite"}, @@ -138,4 +143,4 @@ def close_browser(*_):pass ) ) ) - ) \ No newline at end of file + ) diff --git a/tests/allure_robotframework/acceptance/allure_api/description/description_test.py b/tests/allure_robotframework/acceptance/allure_api/description/description_test.py index 18a43d24..e3a8fb76 100644 --- a/tests/allure_robotframework/acceptance/allure_api/description/description_test.py +++ b/tests/allure_robotframework/acceptance/allure_api/description/description_test.py @@ -1,10 +1,11 @@ """ ./allure-robotframework/examples/description.rst """ from hamcrest import assert_that -from tests.allure_robotframework.conftest import AllureRobotRunner +from tests.allure_robotframework.robot_runner import AllureRobotRunner from allure_commons_test.report import has_test_case from allure_commons_test.result import has_description + def test_single_line_description_from_setting( robot_runner: AllureRobotRunner ): @@ -57,4 +58,4 @@ def test_miltiline_description_from_keyword( "This documentation will appear as allure description." ) ) - ) \ No newline at end of file + ) diff --git a/tests/allure_robotframework/acceptance/allure_api/labels/labels_test.py b/tests/allure_robotframework/acceptance/allure_api/labels/labels_test.py index 0f7ed8a0..e5f1782d 100644 --- a/tests/allure_robotframework/acceptance/allure_api/labels/labels_test.py +++ b/tests/allure_robotframework/acceptance/allure_api/labels/labels_test.py @@ -2,7 +2,7 @@ from allure import severity_level from hamcrest import assert_that -from tests.allure_robotframework.conftest import AllureRobotRunner +from tests.allure_robotframework.robot_runner import AllureRobotRunner from allure_commons_test.report import has_test_case from allure_commons_test.label import has_label from allure_commons_test.label import has_feature @@ -10,6 +10,7 @@ from allure_commons_test.label import has_severity from allure_commons_test.label import has_suite + def test_custom_label_from_robot_tag(robot_runner: AllureRobotRunner): robot_runner.run_robotframework( suite_rst_ids={"labels.robot": "tag-custom-robot"} @@ -69,4 +70,4 @@ def test_labels_from_test_library(robot_runner: AllureRobotRunner): has_label("endpoint", "localhost:443"), has_suite("Testing API at localhost") ) - ) \ No newline at end of file + ) diff --git a/tests/allure_robotframework/acceptance/allure_api/links/links_test.py b/tests/allure_robotframework/acceptance/allure_api/links/links_test.py index 8196c0b4..8bcac5c7 100644 --- a/tests/allure_robotframework/acceptance/allure_api/links/links_test.py +++ b/tests/allure_robotframework/acceptance/allure_api/links/links_test.py @@ -1,12 +1,13 @@ """ ./allure-robotframework/examples/link.rst """ from hamcrest import assert_that -from tests.allure_robotframework.conftest import AllureRobotRunner +from tests.allure_robotframework.robot_runner import AllureRobotRunner from allure_commons_test.report import has_test_case from allure_commons_test.result import has_link from allure_commons_test.result import has_issue_link from allure_commons_test.result import has_test_case_link + def test_link_from_robot_tag(robot_runner: AllureRobotRunner): robot_runner.run_robotframework( suite_rst_ids={"links.robot": "tag-unnamed-robot"} @@ -138,4 +139,4 @@ def test_rf_specific_tag_syntax(docstring, robot_runner: AllureRobotRunner): "https://github.com/allure-framework/allure-python/issues" ) ) - ) \ No newline at end of file + ) diff --git a/tests/allure_robotframework/acceptance/allure_api/steps/steps_test.py b/tests/allure_robotframework/acceptance/allure_api/steps/steps_test.py index 179f7f9f..2daeee49 100644 --- a/tests/allure_robotframework/acceptance/allure_api/steps/steps_test.py +++ b/tests/allure_robotframework/acceptance/allure_api/steps/steps_test.py @@ -1,7 +1,7 @@ """ ./allure-robotframework/examples/step.rst """ from hamcrest import assert_that -from tests.allure_robotframework.conftest import AllureRobotRunner +from tests.allure_robotframework.robot_runner import AllureRobotRunner from allure_commons_test.report import has_test_case from allure_commons_test.result import has_step @@ -24,4 +24,4 @@ def test_library_steps(robot_runner: AllureRobotRunner): ) ) ) - ) \ No newline at end of file + ) diff --git a/tests/allure_robotframework/acceptance/allure_api/tags/tags_test.py b/tests/allure_robotframework/acceptance/allure_api/tags/tags_test.py index 7fb4d6b0..1504630d 100644 --- a/tests/allure_robotframework/acceptance/allure_api/tags/tags_test.py +++ b/tests/allure_robotframework/acceptance/allure_api/tags/tags_test.py @@ -1,11 +1,12 @@ """ ./allure-robotframework/examples/tag.rst """ from hamcrest import assert_that, all_of, not_ -from tests.allure_robotframework.conftest import AllureRobotRunner +from tests.allure_robotframework.robot_runner import AllureRobotRunner from allure_commons_test.report import has_test_case from allure_commons_test.label import has_tag from allure_commons_test.label import has_label + def test_robot_tags(robot_runner: AllureRobotRunner): robot_runner.run_robotframework( suite_rst_ids={"tags.robot": "tags-static-robot"} @@ -108,4 +109,4 @@ def test_tags_from_library(robot_runner: AllureRobotRunner): has_tag("legacy") ) ) - ) \ No newline at end of file + ) diff --git a/tests/allure_robotframework/acceptance/allure_api/testplan/testplan_test.py b/tests/allure_robotframework/acceptance/allure_api/testplan/testplan_test.py index 49c44d51..e464c79c 100644 --- a/tests/allure_robotframework/acceptance/allure_api/testplan/testplan_test.py +++ b/tests/allure_robotframework/acceptance/allure_api/testplan/testplan_test.py @@ -2,7 +2,8 @@ from allure_commons_test.report import has_test_case from hamcrest import assert_that, all_of, not_ -from tests.allure_robotframework.conftest import AllureRobotRunner +from tests.allure_robotframework.robot_runner import AllureRobotRunner + def test_testplan(robot_runner: AllureRobotRunner): robot_runner.run_robotframework( @@ -20,4 +21,4 @@ def test_testplan(robot_runner: AllureRobotRunner): has_test_case("Testplan.Not Selected") ) ) - ) \ No newline at end of file + ) diff --git a/tests/allure_robotframework/acceptance/robotframework_support/fixtures/fixture_test.py b/tests/allure_robotframework/acceptance/robotframework_support/fixtures/fixture_test.py index 29180cba..d20e266e 100644 --- a/tests/allure_robotframework/acceptance/robotframework_support/fixtures/fixture_test.py +++ b/tests/allure_robotframework/acceptance/robotframework_support/fixtures/fixture_test.py @@ -1,5 +1,5 @@ from hamcrest import assert_that -from tests.allure_robotframework.conftest import AllureRobotRunner +from tests.allure_robotframework.robot_runner import AllureRobotRunner from allure_commons_test.report import has_test_case from allure_commons_test.container import has_container from allure_commons_test.container import has_before @@ -8,6 +8,7 @@ from allure_commons_test.result import has_status_details from allure_commons_test.result import with_message_contains + def test_setup(docstring, robot_runner: AllureRobotRunner): """ *** Keywords *** @@ -220,4 +221,3 @@ def test_failed_setup_teardown(docstring, robot_runner: AllureRobotRunner): ) ) ) - diff --git a/tests/allure_robotframework/acceptance/robotframework_support/statuses/statuses_test.py b/tests/allure_robotframework/acceptance/robotframework_support/statuses/statuses_test.py index a66ad43b..2f65fd48 100644 --- a/tests/allure_robotframework/acceptance/robotframework_support/statuses/statuses_test.py +++ b/tests/allure_robotframework/acceptance/robotframework_support/statuses/statuses_test.py @@ -1,6 +1,6 @@ from hamcrest import assert_that from doctest import script_from_examples -from tests.allure_robotframework.conftest import AllureRobotRunner +from tests.allure_robotframework.robot_runner import AllureRobotRunner from allure_commons_test.report import has_test_case from allure_commons_test.result import with_status from allure_commons_test.result import has_status_details @@ -8,6 +8,7 @@ from allure_commons_test.result import with_trace_contains from allure_commons_test.result import has_step + def test_failed_test(docstring, robot_runner: AllureRobotRunner): """ *** Test Cases *** @@ -97,4 +98,4 @@ def test_steps_after_failed_are_skipped(docstring, robot_runner: AllureRobotRunn with_status("skipped") ) ) - ) \ No newline at end of file + ) diff --git a/tests/allure_robotframework/conftest.py b/tests/allure_robotframework/conftest.py index bc968eea..3bccc8fd 100644 --- a/tests/allure_robotframework/conftest.py +++ b/tests/allure_robotframework/conftest.py @@ -1,192 +1,7 @@ -import shutil -import robot -import json -from pathlib import Path -from pytest import FixtureRequest, Pytester, MonkeyPatch, fixture -from tests.conftest import fake_logger -from typing import Sequence, Mapping -from tests.conftest import RstExampleTable -from allure_robotframework import allure_robotframework - -class AllureRobotRunner: - LOGGER_PATH = "allure_robotframework.robot_listener.AllureFileLogger" - - def __init__( - self, - request: FixtureRequest, - pytester: Pytester, - monkeypatch: MonkeyPatch - ) -> None: - self.request = request - self.pytester = pytester - self.monkeypatch = monkeypatch - self.allure_results = None - - def run_robotframework( - self, - suite_paths: Sequence[str|Path] = None, - suite_literals: Mapping[str, str] = None, - suite_rst_ids: Sequence[str] = None, - library_paths: Sequence[str|Path] = None, - library_literals: Mapping[str, str] = None, - library_rst_ids: Sequence[str] = None, - testplan: Mapping[str, any] = None, - testplan_path: str|Path = None, - testplan_rst_id: str = None, - options: Mapping[str, any] = None - ) -> None: - """Runs the robotframework against an example. - - The example consists of suite(s), i.e., .robot files, libraries, - i.e., .py files and, optionaly, a testplan. All types of files can be - specified either as paths or as a mapping from a file name to its content - or as a mapping from a file name to an ID in the associated .rst - document. - - Arguments: - suite_paths (Sequence[str|Path]): a sequence of path-like objects - pointing to .robot files. - suite_literals (Mapping[str, str]): a mapping from a file name to - the content of a .robot file (.robot extension can be omitted). - suite_rst_ids (Mapping[str, str]): a mapping from a file name to an - ID of a .robot file content from the .rst document, associated - with current node. - library_paths (Sequence[str|Path]): a sequence of path-like objects - pointing to .py library files. - library_literals (Mapping[str, str]): a mapping from a file name to - the content of a .py library file (.py extension can be - omitted). - library_rst_ids (Mapping[str, str]): a mapping from a file name to - an IDs of a .py library file content from the .rst document, - associated with current node. - testplan (Mapping[str, any]): a testplan content. - testplan_path (str|Path): a path to a testplan. - testplan_rst_id (str): a testplan ID from the .rst document, - associated with current node. - options (Mapping[str, any]): robot framework options. - - The result of the run is accessible through the :code:`allure_results` - attribute. - """ - - suites, testplan_path = self.__prepare_example( - suite_literals=suite_literals, - suite_paths=suite_paths, - suite_rst_ids=suite_rst_ids, - library_literals=library_literals, - library_paths=library_paths, - library_rst_ids=library_rst_ids, - testplan=testplan, - testplan_path=testplan_path, - testplan_rst_id=testplan_rst_id, - ) - if testplan_path: - self.__run_with_testplan(suites, testplan_path, options) - else: - self.__run(suites, options) - - def __resolve_options(self, options): - return { - ** { - "listener": allure_robotframework(None), - "log": None, - "loglevel": "DEBUG", - "report": None, - "output": None, - "extension": "robot", - "outputdir": str(self.pytester.path) - }, - ** (options or {}) - } - - def __prepare_example( - self, - suite_literals, - suite_paths, - suite_rst_ids, - library_literals, - library_paths, - library_rst_ids, - testplan, - testplan_path, - testplan_rst_id - ): - suites = self.__make_files_from_literals(".robot", suite_literals) - suites.extend( - self.__copy_files(suite_paths) - ) - suites.extend( - self.__make_all_files_from_rst(".robot", suite_rst_ids) - ) - self.__make_files_from_literals(".py", library_literals) - self.__copy_files(library_paths) - self.__make_all_files_from_rst(".py", library_rst_ids) - testplan_path = self.__prepare_testplan( - testplan_path, - testplan, - testplan_rst_id - ) - return suites, testplan_path - - def __run_with_testplan(self, suites, testplan_path, options): - self.monkeypatch.setenv("ALLURE_TESTPLAN_PATH", str(testplan_path)) - self.__run(suites, options) - - def __run(self, suites, options): - with fake_logger(AllureRobotRunner.LOGGER_PATH) as results: - options = self.__resolve_options(options) - robot.run(*suites, **options) - self.allure_results = results - - def __make_files_from_literals(self, extension, literals): - return [ - self.pytester.makefile( - extension, - **{name: content} - ) for name, content in (literals or {}).items() - ] - - def __copy_files(self, src_paths): - dst_paths = [] - for p in src_paths or []: - src = self.request.path / p - dst = self.pytester.path / Path(p).name - shutil.copyfile(src, dst) - dst_paths.append(dst) - return dst_paths - - def __make_all_files_from_rst(self, extension, rst_name_to_id): - return [ - self.__make_file_from_rst( - extension, - name, rst_id - ) for name, rst_id in (rst_name_to_id or {}).items() - ] - - def __make_file_from_rst(self, extension, name, rst_id): - return self.pytester.makefile( - extension, **{ - name: self.__get_from_rst(rst_id) - } - ) - - def __get_from_rst(self, rst_id): - return RstExampleTable.find_examples(self.request)[rst_id] - - def __prepare_testplan(self, content, path, rst_id): - if content: - path = self.pytester.makefile( - ".json", - json.dumps(content) - ) - elif rst_id: - path = self.pytester.makefile( - ".json", - self.__get_from_rst(rst_id) - ) - return path +from pytest import fixture +from .robot_runner import AllureRobotRunner @fixture -def robot_runner(request, pytester, monkeypatch): - return AllureRobotRunner(request, pytester, monkeypatch) \ No newline at end of file +def robot_runner(request, pytester): + return AllureRobotRunner(request, pytester) diff --git a/tests/allure_robotframework/robot_runner.py b/tests/allure_robotframework/robot_runner.py new file mode 100644 index 00000000..c66fc92d --- /dev/null +++ b/tests/allure_robotframework/robot_runner.py @@ -0,0 +1,122 @@ +import robot +from pathlib import Path +from pytest import FixtureRequest, Pytester +from tests.e2e import AllureFrameworkRunner +from typing import Sequence, Mapping +from allure_robotframework import allure_robotframework + + +class AllureRobotRunner(AllureFrameworkRunner): + """The runner to test allure-robotframework integration.""" + + LOGGER_PATH = "allure_robotframework.robot_listener.AllureFileLogger" + + def __init__(self, request: FixtureRequest, pytester: Pytester): + super().__init__(request, pytester) + + def run_robotframework( + self, + suite_paths: Sequence[str | Path] = None, + suite_literals: Mapping[str, str] = None, + suite_rst_ids: Sequence[str] = None, + library_paths: Sequence[str | Path] = None, + library_literals: Mapping[str, str] = None, + library_rst_ids: Sequence[str] = None, + testplan_content: dict = None, + testplan_path: str | Path = None, + testplan_rst_id: str = None, + options: Mapping[str, any] = None + ) -> None: + """Runs the robotframework against an example. + + The example consists of suite(s), i.e., .robot files, libraries, + i.e., .py files and, optionaly, a testplan. All types of files can be + specified either as paths or as a mapping from a file name to its content + or as a mapping from a file name to an ID in the associated .rst + document. + + Arguments: + suite_paths (Sequence[str | Path]): a sequence of path-like objects + pointing to .robot files relative to the current test`s folder. + suite_literals (Mapping[str, str]): a mapping from a file name to + the content of a .robot file (.robot extension can be omitted). + suite_rst_ids (Mapping[str, str]): a mapping from a file name to an + ID of a .robot file content from the .rst document, associated + with current node. + library_paths (Sequence[str | Path]): a sequence of path-like + objects pointing to .py library files relative to the current + test`s folder. + library_literals (Mapping[str, str]): a mapping from a file name to + the content of a .py library file (.py extension can be + omitted). + library_rst_ids (Mapping[str, str]): a mapping from a file name to + an IDs of a .py library file content from the .rst document, + associated with current node. + testplan (dict): a testplan content. + testplan_path (str | Path): a path to a testplan relative to the + current test`s folder. + testplan_rst_id (str): a testplan ID from the .rst document, + associated with current node. + options (Mapping[str, any]): robot framework options. + + Returns: + allure_commons.logger.AllureMemoryLogger: the allure results of the + run. The results of the last run are also accessible through the + :attr:`allure_results` attribute. + """ + return self._run( + self.__prepare_example( + suite_literals=suite_literals, + suite_paths=suite_paths, + suite_rst_ids=suite_rst_ids, + library_literals=library_literals, + library_paths=library_paths, + library_rst_ids=library_rst_ids + ), + testplan_content=testplan_content, + testplan_path=testplan_path, + testplan_rst_id=testplan_rst_id, + logger_path=AllureRobotRunner.LOGGER_PATH, + options=self.__resolve_options(options) + ) + + def _run_framework(self, suites, options): + robot.run(*suites, listener=allure_robotframework(None), **options) + + def __resolve_options(self, options): + return { + ** { + "log": None, + "loglevel": "DEBUG", + "report": None, + "output": None, + "extension": "robot", + "outputdir": str(self.pytester.path) + }, + ** (options or {}) + } + + def __prepare_example( + self, + suite_literals, + suite_paths, + suite_rst_ids, + library_literals, + library_paths, + library_rst_ids + ): + suites = self._create_files( + extension=".robot", + paths=suite_paths, + literals=suite_literals, + rst_examples=suite_rst_ids + ) + self._create_files( + paths=library_paths, + literals=library_literals, + rst_examples=library_rst_ids + ) + return suites + + +__all__ = ["AllureRobotRunner"] diff --git a/tests/conftest.py b/tests/conftest.py index dc8b4109..efd47e01 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,285 +1,10 @@ import pytest -import mock -import shutil -import runpy -import warnings -import docutils.nodes -import docutils.parsers.rst -import docutils.utils -import docutils.frontend -from pathlib import Path -from doctest import script_from_examples -from contextlib import contextmanager -from typing import Mapping, Tuple, TypeVar -from pytest import FixtureRequest, Pytester - -import allure_commons -from allure_commons_test.report import AllureReport -from allure_commons.logger import AllureMemoryLogger +from pytest import FixtureRequest +from .e2e import find_node_with_docstring_or_throw, RstExampleTable pytest_plugins = "pytester" -def pytest_configure(config: pytest.Config): - config.addinivalue_line( - "markers", "real_logger: mark test to run with a real allure logger" - ) - - -@contextmanager -def fake_logger(path :str = None) -> None: - if path is None: - path = "allure_commons.logger.AllureFileLogger" - blocked_plugins = [] - for name, plugin in allure_commons.plugin_manager.list_name_plugin(): - allure_commons.plugin_manager.unregister(plugin=plugin, name=name) - blocked_plugins.append(plugin) - - with mock.patch(path) as ReporterMock: - ReporterMock.return_value = AllureMemoryLogger() - yield ReporterMock.return_value - - for plugin in blocked_plugins: - allure_commons.plugin_manager.register(plugin) - - -def get_docstring(node: pytest.Item) -> str: - if isinstance(node, pytest.Function): - return node.function.__doc__ - elif isinstance(node, pytest.Class): - return node.cls.__doc__ - elif isinstance(node, pytest.Module): - return node.module.__doc__ - elif isinstance(node, pytest.Package): - return node.obj.__doc__ - return None - - -def find_node_with_docstring( - request: FixtureRequest -) -> Tuple[pytest.Item, str]: - node = request.node - while node: - docstring = get_docstring(node) - if docstring: - break - node = node.parent - return node, docstring - - -def find_node_with_docstring_or_throw( - request: FixtureRequest -) -> Tuple[pytest.Item, str]: - node, docstring = find_node_with_docstring(request) - if node is None: - nodeid = request.node.nodeid - raise ValueError(f"Unable to get docstring for node {nodeid}") - return node, docstring - - -def get_path_from_docstring(request: FixtureRequest) -> Path: - return request.config.rootpath.joinpath( - find_node_with_docstring_or_throw(request)[1].strip() - ) - -RstExampleTableT = TypeVar("RstExampleTableT", bound="RstExampleTable") -class RstExampleTable: - - STASH_KEY = pytest.StashKey() - - def __init__(self, filepath: str|Path) -> None: - self.source = filepath - self.examples = self.load_examples(filepath) - - def __getitem__(self, name: str) -> str: - if name not in self.examples: - raise KeyError(f"Code block {name!r} not found in {self.source}") - return self.examples[name] - - @staticmethod - def find_examples(request: FixtureRequest) -> RstExampleTableT: - """Loads code examples, associated with the test node or one of its - parents. - - Arguments: - request (FixtureRequest): the request fixture - - Returns (RstExampleTable): A table containing the examples, indexed by - their names from the .rst document - - Notes: - It first finds a docstring defined on the function, class, module or - package associated with the node. If multiple docstrings exist, - the one defined on a lower level wins. - - The docstring content is used then as the path to the - reStructuredText document. The document is parsed and all its code - blocks are combined in a dictionary mapping their names to the code - blocks themselves. The mapping can be accessed through the returned - instance of RstExampleTable class. - - The table is then cached in a stash of the same node from which the - original docstring was loaded. This prevents from parsing the same - document several times if multiple tests from the same module/package - (or parametrizations of the same metafunc) are testing the examples - from the same document. - """ - - node, docstring = find_node_with_docstring_or_throw( - request - ) - examples = RstExampleTable.__get_from_cache(node) - if examples is None: - examples = RstExampleTable.__create_and_cache(node, docstring) - return examples - - @staticmethod - def load_examples(filepath: str|Path) -> Mapping[str, str]: - document = RstExampleTable.parse_rst(filepath) - return { - name: code_block.astext() - for code_block in document.findall( - RstExampleTable.__filter - ) for name in code_block["names"] - } - - @staticmethod - def parse_rst(filepath: str|Path) -> docutils.nodes.document: - with warnings.catch_warnings(): - warnings.simplefilter("ignore", DeprecationWarning) - parser = docutils.parsers.rst.Parser() - components = (docutils.parsers.rst.Parser,) - settings = docutils.frontend.OptionParser( - components=components - ).get_default_values() - document = docutils.utils.new_document( - str(filepath), - settings=settings - ) - with open(filepath, encoding="utf-8") as f: - content = f.read() - parser.parse(content, document) - return document - - @staticmethod - def __get_from_cache(node): - if RstExampleTable.STASH_KEY in node.stash: - return node.stash[RstExampleTable.STASH_KEY] - - @staticmethod - def __create_and_cache(node, path): - filepath = node.config.rootpath.joinpath(path.strip()) - if not filepath.exists() or filepath.is_dir(): - raise ValueError( - f"Document, referred by {node.nodeid}, " - f"doesn't exist at {filepath}" - ) - table = RstExampleTable(filepath) - node.stash[RstExampleTable.STASH_KEY] = table - return table - - @staticmethod - def __filter(node): - return isinstance( - node, - docutils.nodes.literal_block - ) and "code" in node["classes"] and node["names"] - - -class AlluredTestdir: - def __init__(self, pytester: Pytester, request: FixtureRequest): - self.testdir = pytester - self.request = request - self.allure_report = None - self.plugins = ["allure_pytest", "allure_pytest_bdd"] - self.enabled_plugins = {"allure_pytest"} - - def select_plugins(self, *plugins: str): - self.enabled_plugins = set(plugins) - - def get_plugin_args(self): - for plugin_package in self.plugins: - yield "-p" - if plugin_package in self.enabled_plugins: - yield plugin_package - else: - yield f"no:{plugin_package}" - - def parse_docstring_source(self): - _, docstring = find_node_with_docstring_or_throw(self.request) - source = script_from_examples(docstring).replace("#\n", "\n") - return self.testdir.makepyfile(source) - - def parse_docstring_path(self): - example_file = get_path_from_docstring(self.request) - with open(example_file, encoding="utf-8") as f: - content = f.read() - source = script_from_examples(content) - return self.testdir.makepyfile(source) - - def copy_docstring_dir(self): - example_dir = get_path_from_docstring(self.request) - return shutil.copytree( - example_dir, - self.testdir.path, - symlinks=True, - dirs_exist_ok=True - ) - - def run_with_allure(self, *args: str, **kwargs: str): - pytest_args = [ - "--alluredir", - self.testdir.path, - *self.get_plugin_args(), - *args - ] - if self.request.node.get_closest_marker("real_logger"): - self.testdir.runpytest(*pytest_args, **kwargs) - self.allure_report = AllureReport(self.testdir.path) - else: - with fake_logger() as logger: - self.allure_report = logger - self.testdir.runpytest(*pytest_args, **kwargs) - - return self.allure_report - - -@pytest.fixture -def allured_testdir( - pytester: Pytester, - request: FixtureRequest -) -> AlluredTestdir: - fixture = AlluredTestdir(pytester, request) - return fixture - - -@pytest.fixture -def executed_docstring_source( - allured_testdir: AlluredTestdir -) -> AlluredTestdir: - allured_testdir.parse_docstring_source() - allured_testdir.run_with_allure() - return allured_testdir - - -@pytest.fixture -def executed_docstring_path( - allured_testdir: AlluredTestdir -) -> AlluredTestdir: - allured_testdir.parse_docstring_path() - allured_testdir.run_with_allure() - return allured_testdir - - -@pytest.fixture -def executed_docstring_directory( - allured_testdir: AlluredTestdir -) -> AlluredTestdir: - allured_testdir.copy_docstring_dir() - allured_testdir.run_with_allure() - return allured_testdir - - @pytest.fixture def rst_examples(request: FixtureRequest) -> RstExampleTable: return RstExampleTable.find_examples(request) diff --git a/tests/e2e.py b/tests/e2e.py new file mode 100644 index 00000000..3b1cfdd0 --- /dev/null +++ b/tests/e2e.py @@ -0,0 +1,615 @@ +"""Utility functions and classes for end-to-end testing of allure integrations +with python testing frameworks. + +""" + +import docutils +import docutils.nodes +import docutils.parsers.rst +import json +import mock +import pytest +import shutil +import warnings +from abc import abstractmethod +from contextlib import contextmanager +from pathlib import Path +from pytest import FixtureRequest, Pytester, MonkeyPatch +from typing import Tuple, Mapping, TypeVar, Generator, Callable + +import allure_commons +from allure_commons.logger import AllureMemoryLogger +from allure_commons_test.report import AllureReport + + +@contextmanager +def altered_env(**kwargs) -> Generator[MonkeyPatch, None, None]: + mp = MonkeyPatch() + for n, v in kwargs.items(): + if v is None: + mp.delenv(n, False) + else: + mp.setenv(n, str(v)) + yield mp + mp.undo() + + +@contextmanager +def allure_plugin_context(): + """Separates an allure integration under test from currently active + allure integration (if eny). + + """ + + outer_context_plugins = __pop_all_allure_plugins() + + yield + + __pop_all_allure_plugins() + __restore_allure_plugins(outer_context_plugins) + + +@contextmanager +def allure_in_memory_context( + path: str = None +) -> Generator[AllureMemoryLogger, None, None]: + """Creates a context to test an allure integration. + + While a testing context is active the following conditions hold: + + #. Allure's output is stored in memory instead of being written to the file + system. + #. Allure plugins of an integration under test are isolated from the + allure plugins of the executing environment (if allure is active). + + When the testing context is deactivated, all allure plugins registered + during the test are removed and the plugins of the execution environment are + restored. + + Arguments: + path (str): a path to a class to replace. + Defaults to :code:`"allure_commons.logger.AllureFileLogger"`. + Provide this argument if the integration under test imports the + logger using the + :code:`from allure_commons.logger import AllureFileLogger` syntax. + + Yields: + AllureMemoryLogger: an instance of the in-memory logger, where an output + will be collected. + + """ + + if path is None: + path = "allure_commons.logger.AllureFileLogger" + + # Plugin context must be set first, because mock patching may cause + # module loading, thus, side effects, including allure decorators evaluation + # (and that requires all plugins of nested allure to already be in place). + with allure_plugin_context(): + with mock.patch(path) as ReporterMock: + ReporterMock.return_value = AllureMemoryLogger() + yield ReporterMock.return_value + + +class AllureFileContextValue: + def __init__(self): + self.allure_results = None + + +@contextmanager +def allure_file_context( + alluredir: str | Path +) -> Generator[AllureFileContextValue, None, None]: + """Creates a context to test an allure integration. + + It behaves in a way similar to :func:`allure_in_memory_context` except that + the result is created from the actual allure output files. It is useful + if the result files are generated in a separate process. + + Arguments: + alluredir (str | Path): a path to allure results directory. + + The report can be accessed through the :attr:`AllureFileContextValue.output` + attribute once the context is exited. + + Example: + + >>> with allure_file_context("./allure-results") as v: + ... # run framework with allure integration + >>> # use v.allure_results to access the results + + """ + + with allure_plugin_context(): + value = AllureFileContextValue() + yield value + value.allure_results = AllureReport(alluredir) + + +def find_node_with_docstring( + request: FixtureRequest +) -> Tuple[pytest.Item, str] | Tuple[None, None]: + """Find a docstring associated with a test function. + + It first checks the function itself and then, if no docstring was found, + moves up the hierarchy until either a docstring is found or no parent nodes + left. + + Returns: + A tuple of a node and its docstring or :code:`(None, None)` if no + docstring is found. + + """ + + node = request.node + while node: + docstring = node.obj.__doc__ + if docstring: + break + node = node.parent + return node, docstring + + +def find_node_with_docstring_or_throw( + request: FixtureRequest +) -> Tuple[pytest.Item, str]: + """Find a docstring associated with a test function. + + It first checks the function itself and then, if no docstring was found, + moves up the hierarchy until either a docstring is found or no parent nodes + left. + + Raises: + ValueError: if the docstring doesn't exist. + + Returns: + A tuple of a node and its docstring. + + """ + node, docstring = find_node_with_docstring(request) + if node is None: + nodeid = request.node.nodeid + raise ValueError(f"Unable to get docstring for node {nodeid}") + return node, docstring + + +def get_path_from_docstring(request: FixtureRequest) -> Path: + """Extract a path to a file or folder from a docstring of a test function or + one of its parents. + + The path is joined to the rootpath. + + Raises: + ValueError: if no docstring specified. + + Returns: + Path: a full path path denoted by the docstring. + + """ + return request.config.rootpath.joinpath( + find_node_with_docstring_or_throw(request)[1].strip() + ) + + +RstExampleTableT = TypeVar("RstExampleTableT", bound="RstExampleTable") + + +class RstExampleTable: + """Examples from a .rst document associated with a test. + + Attributes: + STASH_KEY (pytest.StashKey): (class attribute) a pytest stash key to + cache an instance of this class on node, that established the + association with the document. + source (str | Path): a path to a document the table was created from. + examples (Mapping[str, str]): a mapping from an example name to its + content. + + """ + + STASH_KEY = pytest.StashKey() + + def __init__(self, filepath: str | Path) -> None: + """Create a table of examples from a .rst document. + + Arguments: + filepath (str | Path): a path to a .rst document to parse. + + """ + self.source = filepath + self.examples = self.__load_examples(filepath) + + def __getitem__(self, name: str) -> str: + """Find an example in the document by its name. + + An example is represented by a code block in the original document. The + name of the example is specified using the :code:`:name:` option of the + code block. + + Arguments: + name (str): a name of an example. + + Raises: + KeyError: the required example doesn't exist in the document. + + Returns: + str: the content of the example. + """ + if name not in self.examples: + raise KeyError(f"Code block {name!r} not found in {self.source}") + return self.examples[name] + + @staticmethod + def find_examples(request: FixtureRequest) -> RstExampleTableT: + """Loads code examples, associated with the test node or one of its + parents. + + Arguments: + request (FixtureRequest): the request fixture. + + Returns (RstExampleTable): A table containing the examples, indexed by + their names from the .rst document + + Notes: + It first finds a docstring defined on the function, class, module or + package associated with the node. If multiple docstrings exist, + the one defined on a lower level wins. + + The docstring content is used then as the path to the + reStructuredText document. The document is parsed and all its code + blocks are combined in a dictionary that maps their names to the + code block content. The mapping can be accessed through the returned + instance of RstExampleTable class. + + The table is then cached in a stash of the same node from which the + original docstring was loaded. This prevents from parsing the same + document several times if multiple tests from the same module/package + (or parametrizations of the same metafunc) are testing the examples + from the same document. + """ + + node, docstring = find_node_with_docstring_or_throw( + request + ) + examples = RstExampleTable.__get_from_cache(node) + if examples is None: + examples = RstExampleTable.__create_and_cache(node, docstring) + return examples + + @staticmethod + def __load_examples(filepath: str | Path) -> Mapping[str, str]: + document = RstExampleTable.parse_rst(filepath) + return { + name: code_block.astext() + for code_block in document.findall( + RstExampleTable.__filter + ) for name in code_block["names"] + } + + @staticmethod + def parse_rst(filepath: str | Path) -> docutils.nodes.document: + with warnings.catch_warnings(): + warnings.simplefilter("ignore", DeprecationWarning) + parser = docutils.parsers.rst.Parser() + components = (docutils.parsers.rst.Parser,) + settings = docutils.frontend.OptionParser( + components=components + ).get_default_values() + document = docutils.utils.new_document( + str(filepath), + settings=settings + ) + with open(filepath, encoding="utf-8") as f: + content = f.read() + parser.parse(content, document) + return document + + @staticmethod + def __get_from_cache(node): + if RstExampleTable.STASH_KEY in node.stash: + return node.stash[RstExampleTable.STASH_KEY] + + @staticmethod + def __create_and_cache(node, path): + filepath = node.config.rootpath.joinpath(path.strip()) + if not filepath.exists() or filepath.is_dir(): + raise ValueError( + f"Document, referred by {node.nodeid}, " + f"doesn't exist at {filepath}" + ) + table = RstExampleTable(filepath) + node.stash[RstExampleTable.STASH_KEY] = table + return table + + @staticmethod + def __filter(node): + return isinstance( + node, + docutils.nodes.literal_block + ) and "code" in node["classes"] and node["names"] + + +class AllureFrameworkRunner: + """An abstract base class for framework test runners to test allure + integrations. + + """ + def __init__(self, request: FixtureRequest, pytester: Pytester): + self.request = request + self.pytester = pytester + self.allure_results = None + self.in_memory = True + + def _run( + self, + *args, + testplan_content: dict = None, + testplan_path: str | Path = None, + testplan_rst_id: str = None, + logger_path: str = None, + **kwargs + ) -> AllureMemoryLogger: + """Runs the framework and collect the allure results. + + Prepares the testplan (if any), replace the allure file logger with the + in-memory logger and runs the :method:`_run_framework` method. + + Arguments: + *args: positional arguments for the underlying call. + + Keyword arguments: + testplan_content (dict): the content of the allure testplan. + testplan_path (str | Path): the path to the allure testplan + relative to the current test folder. + testplan_rst_id (str): the ID of the allure testplan code block. + **kwargs: keyword arguments for the underlying call. + + Returns: + AllureMemoryLogger: the collected allure results. The results of the + last call are also available in the :attr:`allure_results` + attribute. + + """ + testplan_path = self.__prepare_testplan( + content=testplan_content, + path=testplan_path, + rst_id=testplan_rst_id + ) + with altered_env(ALLURE_TESTPLAN_PATH=testplan_path): + output = self.__run_and_collect_results_in_memory( + logger_path, + args, + kwargs + ) if self.in_memory else self.__run_and_collect_results_from_fs( + args, + kwargs + ) + self.allure_results = output + return output + + @abstractmethod + def _run_framework(self, *args, **kwargs): + """Override this method and invoke the actual framework. + + This method is invoked by the :method:`_run`. Arguments (args and kwargs + are the same as were provided to the :method:`_run`. + + """ + + def _find_docstring(self): + """Find a docstring, associated with the test ot throw.""" + return find_node_with_docstring_or_throw(self.request)[1] + + def _get_all_content(self, paths=None, literals=None, rst_ids=None): + """Return a list of content denoted by files, literals and IDs of the + .rst document code blocks. + + Arguments: + paths (Sequence[str | Path]): a sequence of file paths (relative to + the current test's directory) to load content from. + literals (Sequence[str]): a sequence of literals to use as content. + rst_ids (Sequence[str]): a sequence of code blocks from the .rst + document. + + Returns: + List[str]: a list of strings, each representing a file content. + + """ + + return [ + self._read_file(p) for p in (paths or []) + ] + list(literals or []) + [ + self.__get_from_rst(rst_id) for rst_id in (rst_ids or []) + ] + + def _resolve_content(self, path=None, literal=None, rst_id=None): + """Return content denoted either by a literal or a path (relative to + the test folder) or an ID of the .rst document code block in that + order. The first content source wins. + + """ + if literal: + return literal + if path: + return self._read_file(path) + if rst_id: + return self.__get_from_rst(rst_id) + + def _create_files( + self, + extension=".py", + paths=None, + literals=None, + rst_examples=None + ): + """Create and/or copy files in the pytester directory. + + Arguments: + extension (str): an output files extension. + paths (Sequence[str | Path]): a sequence of paths to copy, + relative to the current test directory. + literals (Mapping[str, str]): a mapping from a name of a file to + its content (an extension can be omitted). + rst_examples (Mapping[str, str]): a mapping from a file name (an + extension may be omitted) to its ID in the .rst document. + + Returns: + List[Path]: a list of the newly created file paths. + """ + return self._copy_files(paths) + self._make_files_from_literals( + extension, + literals + ) + self._make_files_from_rst(extension, rst_examples) + + def _make_files_from_literals(self, extension, literals): + """Create files in the pytester directory. + + Arguments: + extension (str): an output files extension. + literals (Mapping[str, str]): a mapping from a name of a file to + its content (an extension can be omitted). + + Returns: + List[Path]: a list of the newly created file paths. + """ + + return [ + self.pytester.makefile( + extension, + **{name: content} + ) for name, content in (literals or {}).items() + ] + + def _copy_files(self, src_paths): + """Copy files denoted by paths into the pytester directory. + + Arguments: + src_paths (Sequence[str | Path]): a sequence of paths to copy, + relative to the current test directory. + + Returns: + List[Path]: a list of the newly created file paths. + """ + dst_paths = [] + for p in src_paths or []: + src = self.request.path.parent / p + dst = self.pytester.path / Path(p).name + shutil.copyfile(src, dst) + dst_paths.append(dst) + return dst_paths + + def _make_files_from_rst( + self, + extension: str, + rst_name_to_id: Mapping[str, str] + ): + """Copy code blocks from the .rst document to the pytester dir. + + Arguments: + extension (str): an output files extension. + rst_name_to_id (Mapping[str, str]): a mapping from a file name (an + extension may be omitted) to its ID in the .rst document. + + Returns: + List[Path]: a list of the newly created file paths. + """ + + return [ + self.__make_single_file_from_rst( + extension, + name, rst_id + ) for name, rst_id in (rst_name_to_id or {}).items() + ] + + def _read_file(self, path: str | Path, basepath: str | Path = None): + """Read file content. + + Arguments: + path (str | Path): a path to a file. If it's a relative path, it's + prepended by the basepath. + basepath (str | Path): a basepath. The path to the folder of the + current test by default. + + """ + if basepath is None: + basepath = self.request.path.parent + path = basepath / path + with open(path, mode="r", encoding="utf-8") as f: + return f.read() + + def _cache_docstring_test_result( + self, + cache_key: pytest.StashKey, + test_fn: Callable, + *args: any, + **kwargs: any + ): + """Gets docstring run results from the cache of the node associated with + the docstring. In case of a cache miss, returns + :code:`run_fn(*args, docstring, **kwargs)`. The result is cached to + avoid future calls. + + Arguments: + cache_key (pytest.StashKey): a pytest stash key used to cache the + result. + run_fn (Callable): a function to call in case of a cache miss. The + docstring is appended to the positional arguments. + *args: positional arguments of the :param:`run_fn`. + **kwargs: keyword arguments of the :param:`run_fn`. + + Returns: + The result of the :code:`run_fn(*args, docstring, **kwargs)` call, + either from the cache or from the actual call. + """ + + node, docstring = find_node_with_docstring_or_throw(self.request) + if cache_key in node.stash: + return node.stash[cache_key] + result = test_fn(*args, docstring, **kwargs) + node.stash[cache_key] = result + return result + + def __run_and_collect_results_in_memory(self, logger_path, args, kwargs): + with allure_in_memory_context(logger_path) as output: + self._run_framework(*args, **kwargs) + return output + + def __run_and_collect_results_from_fs(self, args, kwargs): + with allure_file_context(self.pytester.path) as v: + self._run_framework(*args, **kwargs) + return v.allure_results + + def __make_single_file_from_rst(self, extension, name, rst_id): + return self.pytester.makefile( + extension, **{ + name: self.__get_from_rst(rst_id) + } + ) + + def __get_from_rst(self, rst_id): + return RstExampleTable.find_examples(self.request)[rst_id] + + def __prepare_testplan(self, content, path, rst_id): + if content: + path = self.pytester.makefile( + ".json", + json.dumps(content) + ) + elif rst_id: + path = self.pytester.makefile( + ".json", + self.__get_from_rst(rst_id) + ) + return path + + +def __pop_all_allure_plugins(): + name_plugin_tuples = allure_commons.plugin_manager.list_name_plugin() + for name, plugin in name_plugin_tuples: + allure_commons.plugin_manager.unregister(plugin=plugin, name=name) + return name_plugin_tuples + + +def __restore_allure_plugins(name_plugin_tuples): + for plugin, name in name_plugin_tuples: + allure_commons.plugin_manager.register(plugin, name) diff --git a/tests/requirements.txt b/tests/requirements.txt deleted file mode 100644 index 87069c06..00000000 --- a/tests/requirements.txt +++ /dev/null @@ -1,3 +0,0 @@ -docutils -pytest -hamcrest From e24b416f6c3fadba86f122862bb5419d80c7cd23 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 24 Feb 2023 01:16:56 +0700 Subject: [PATCH 07/18] Add allure_pytest tests & docs on dynamic parameter's args --- .../examples/parameter/dynamic_parameter.rst | 74 +++++++++++++++++++ .../parametrization/dynamic_parameter_test.py | 70 ++++++++++++++++++ 2 files changed, 144 insertions(+) create mode 100644 allure-pytest/examples/parameter/dynamic_parameter.rst create mode 100644 tests/allure_pytest/acceptance/parametrization/dynamic_parameter_test.py diff --git a/allure-pytest/examples/parameter/dynamic_parameter.rst b/allure-pytest/examples/parameter/dynamic_parameter.rst new file mode 100644 index 00000000..a56cf207 --- /dev/null +++ b/allure-pytest/examples/parameter/dynamic_parameter.rst @@ -0,0 +1,74 @@ +Dynamic parameter +------------------- + +It's possible to dynamically add a parameter to a test result: + + + >>> import allure + + >>> def test_dynamic_parameter(): + ... allure.dynamic.parameter("username", "John Doe") + + +The parameter name and value are shown in the "Parameters" section of the test +details view. Additionally, the value is shown near the test name in the test +tree view. + + +Affecting the history of test execution in Allure TestOps +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Parameters also affect how Allure TestOps keeps history and retry records +across test results. Two test results that differs from each other in a +parameter value are considered as belonging to different test cases, thus +forming separate histories. + +This behavior can be changed with ``excluded`` argument: + + + >>> import allure + ... import os + + >>> def test_excluded_dynamic_parameter(): + ... allure.dynamic.parameter("work-dir", os.getcwd(), excluded=True) + + +Such a parameter isn't taken into account by Allure TestOps when it decides +whether two test results actually belong to a single test case. This is useful +if you want to add environment-related information (such as host names, OS, PID, +paths, versions, etc.) but keep history the same if the information is changed. + + +Masking a sensitive parameter +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A parameter value can be masked with ``mode`` argument. The masked value is shown +as ``******`` in the report. This is useful for sensitive parameters like +passwords or key phrases: + + + >>> import allure + + >>> def test_masked_dynamic_parameter(): + ... allure.dynamic.parameter("password", "qwerty", mode=allure.parameter_mode.MASKED) + + +WARNING: Although the value is masked in the report, it is still present in the +test result files (but not in report files, i.e., the files generated by +allure reporter). + + +Hiding a parameter from the report +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +A parameter can be hidden from the report completely. It still affects the +history of the test case though. This is useful if you want to force Allure +TestOps to distribute otherwise indistinguishable test results across different +test cases: + + + >>> import allure + >>> import socket + + >>> def test_hidden_dynamic_parameter(): + ... allure.dynamic.parameter("hostname", socket.gethostname(), mode=allure.parameter_mode.HIDDEN) diff --git a/tests/allure_pytest/acceptance/parametrization/dynamic_parameter_test.py b/tests/allure_pytest/acceptance/parametrization/dynamic_parameter_test.py new file mode 100644 index 00000000..d4186f55 --- /dev/null +++ b/tests/allure_pytest/acceptance/parametrization/dynamic_parameter_test.py @@ -0,0 +1,70 @@ +""" ./allure-pytest/examples/parameter/dynamic_parameter.rst """ + +from hamcrest import assert_that +from tests.allure_pytest.pytest_runner import AllurePytestRunner + +from allure_commons_test.report import has_test_case +from allure_commons_test.result import ( + has_parameter, + get_parameter_matcher, + with_excluded, + with_mode +) + + +def test_dynamic_parameter(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_dynamic_parameter", + has_parameter("username", "'John Doe'") + ) + ) + + +def test_masked_dynamic_parameter(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_masked_dynamic_parameter", + has_parameter( + "password", + "'qwerty'", + with_mode("masked") + ) + ) + ) + + +def test_hidden_dynamic_parameter(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_hidden_dynamic_parameter", + get_parameter_matcher( + "hostname", + with_mode("hidden") + ) + ) + ) + + +def test_excluded_dynamic_parameter(allure_pytest_runner: AllurePytestRunner): + allure_results = allure_pytest_runner.run_docpath_examples(cache=True) + + assert_that( + allure_results, + has_test_case( + "test_excluded_dynamic_parameter", + get_parameter_matcher( + "work-dir", + with_excluded() + ) + ) + ) From 09001a95f0132e2476b857723130e1798908ee07 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 24 Feb 2023 01:20:46 +0700 Subject: [PATCH 08/18] Add example links to docs. Fix badge links --- README.md | 18 ++++++++++++------ allure-behave/README.rst | 11 ++++++++--- allure-pytest/README.rst | 9 +++++++-- allure-robotframework/README.rst | 10 ++++++++-- 4 files changed, 35 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index f4e541f9..63a90b75 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ The repository contains adaptors for Python-based test frameworks. Documentation is available [online](https://docs.qameta.io/allure-report/), also you can get help at -[gitter channel](https://gitter.im/allure-framework/allure-core) +[gitter channel](https://gitter.im/allure-framework/allure-core). ## Pytest [![Release @@ -13,7 +13,9 @@ Status](https://img.shields.io/pypi/v/allure-pytest)](https://pypi.python.org/py Allure [pytest](http://pytest.org) integration. It's developed as pytest plugin and distributed via -[pypi](https://pypi.python.org/pypi/allure-pytest) +[pypi](https://pypi.python.org/pypi/allure-pytest). + +[Usage examples](/allure-pytest/examples). ## Behave [![Release @@ -23,7 +25,9 @@ Status](https://img.shields.io/pypi/v/allure-behave)](https://pypi.python.org/py Allure [behave](https://behave.readthedocs.io/en/latest/) integration. Just external formatter that produce test results in allure2 format. This package is available on -[pypi](https://pypi.python.org/pypi/allure-behave) +[pypi](https://pypi.python.org/pypi/allure-behave). + +[Usage examples](/allure-behave/examples). ## Robot Framework [![Release @@ -33,9 +37,11 @@ Status](https://img.shields.io/pypi/v/allure-robotframework)](https://pypi.pytho Allure [RobotFramework](http://robotframework.org/) integration. This integration is a [Listener](http://robotframework.org/robotframework/latest/RobotFrameworkUserGuide.html#listener-interface\)) -and does not require changing autotests +and does not require changing autotests. + +[Read more ...](/allure-robotframework/README.rst). -[Read more ...](/allure-robotframework/README.rst) +[Usage examples](/allure-robotframework/examples). ## Pytest-BDD @@ -45,7 +51,7 @@ Status](https://img.shields.io/pypi/v/allure-pytest-bdd)](https://pypi.python.or Allure [pytest-bdd](http://pytest.org) integration. It's developed as pytest plugin and distributed via -[pypi](https://pypi.python.org/pypi/allure-pytest-bdd) +[pypi](https://pypi.python.org/pypi/allure-pytest-bdd). ## Nose2 [![Release diff --git a/allure-behave/README.rst b/allure-behave/README.rst index cfbcf9ae..acecb718 100644 --- a/allure-behave/README.rst +++ b/allure-behave/README.rst @@ -1,9 +1,9 @@ Allure Behave Formatter ======================= -.. image:: https://pypip.in/v/allure-behave/badge.png +.. image:: https://img.shields.io/pypi/v/allure-behave :alt: Release Status :target: https://pypi.python.org/pypi/allure-behave -.. image:: https://pypip.in/d/allure-behave/badge.png +.. image:: https://img.shields.io/pypi/dm/allure-behave :alt: Downloads :target: https://pypi.python.org/pypi/allure-behave @@ -37,4 +37,9 @@ like in example below. ### your code - allure_report("path/to/result/dir") \ No newline at end of file + allure_report("path/to/result/dir") + +Usage examples +-------------- + +See usage examples `here `_. diff --git a/allure-pytest/README.rst b/allure-pytest/README.rst index 3a7c9e27..49923c54 100644 --- a/allure-pytest/README.rst +++ b/allure-pytest/README.rst @@ -1,9 +1,9 @@ Allure Pytest Plugin ==================== -.. image:: https://pypip.in/v/allure-pytest/badge.png +.. image:: https://img.shields.io/pypi/v/allure-pytest :alt: Release Status :target: https://pypi.python.org/pypi/allure-pytest -.. image:: https://pypip.in/d/allure-pytest/badge.png +.. image:: https://img.shields.io/pypi/dm/allure-pytest :alt: Downloads :target: https://pypi.python.org/pypi/allure-pytest @@ -22,3 +22,8 @@ Installation and Usage $ pip install allure-pytest $ py.test --alluredir=%allure_result_folder% ./tests $ allure serve %allure_result_folder% + +Usage examples +-------------- + +See usage examples `here `_. diff --git a/allure-robotframework/README.rst b/allure-robotframework/README.rst index 6f4d5b1d..0035e3b9 100644 --- a/allure-robotframework/README.rst +++ b/allure-robotframework/README.rst @@ -1,9 +1,9 @@ Allure Robot Framework Listener =============================== -.. image:: https://pypip.in/v/allure-robotframework/badge.png +.. image:: https://img.shields.io/pypi/v/allure-robotframework :alt: Release Status :target: https://pypi.python.org/pypi/allure-robotframework -.. image:: https://pypip.in/d/allure-robotframework/badge.png +.. image:: https://img.shields.io/pypi/dm/allure-robotframework :alt: Downloads :target: https://pypi.python.org/pypi/allure-robotframework @@ -39,6 +39,12 @@ Advanced listener settings: - ALLURE_MAX_STEP_MESSAGE_COUNT=5. If robotframework step contains less messages than specified in this setting, each message shows as substep. This reduces the number of attachments in large projects. The default value is zero - all messages are displayed as attachments. +Usage examples +-------------- + +See usage examples `here `_. + + Contributing to allure-robotframework ===================================== From 0c6b083dd54cfe05783177f0182abf1450a1e701 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 24 Feb 2023 01:25:18 +0700 Subject: [PATCH 09/18] Fix flake8-builtins warnings. Add tasks to lint and test all --- .flake8 | 5 +++++ allure-behave/src/listener.py | 2 +- allure-python-commons/src/_allure.py | 4 ++-- allure-robotframework/src/listener/utils.py | 7 +++---- pyproject.toml | 6 +++++- 5 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.flake8 b/.flake8 index 95a292d8..0b443177 100644 --- a/.flake8 +++ b/.flake8 @@ -2,3 +2,8 @@ max-line-length = 120 import-order-style = google inline-quotes = " +exclude = + ./tests/allure_behave/acceptance/**/test-data/** + ./tests/allure_behave/acceptance/behave_support/background/background_steps.py +per-file-ignores = + ./allure-python-commons/src/model2.py:A003 diff --git a/allure-behave/src/listener.py b/allure-behave/src/listener.py index fbcac3cd..dab47789 100644 --- a/allure-behave/src/listener.py +++ b/allure-behave/src/listener.py @@ -217,7 +217,7 @@ def enter(self): self._logger.start_group(group.uuid, group) self._groups.append(group) - def exit(self): + def exit(self): # noqa: A003 group = self._groups.pop() if group.befores or group.afters: self._logger.stop_group(group.uuid) diff --git a/allure-python-commons/src/_allure.py b/allure-python-commons/src/_allure.py index e6dc2cf9..05e01dbd 100644 --- a/allure-python-commons/src/_allure.py +++ b/allure-python-commons/src/_allure.py @@ -66,7 +66,7 @@ def tag(*tags): return label(LabelType.TAG, *tags) -def id(id): +def id(id): # noqa: A001,A002 return label(LabelType.ID, id) @@ -125,7 +125,7 @@ def tag(*tags): Dynamic.label(LabelType.TAG, *tags) @staticmethod - def id(id): + def id(id): # noqa: A003,A002 Dynamic.label(LabelType.ID, id) @staticmethod diff --git a/allure-robotframework/src/listener/utils.py b/allure-robotframework/src/listener/utils.py index a260ca6f..8b308d81 100644 --- a/allure-robotframework/src/listener/utils.py +++ b/allure-robotframework/src/listener/utils.py @@ -2,7 +2,7 @@ from allure_commons.model2 import Status, Label, Parameter, Link from allure_commons.types import LabelType from allure_robotframework.types import RobotStatus -from allure_commons.mapping import parse_tag, labels_set, allure_tag_sep +from allure_commons.mapping import parse_tag, labels_set def get_allure_status(status): @@ -44,16 +44,15 @@ def get_allure_suites(longname): return labels -def get_items_of_type_from_tags(tags, type): +def get_items_of_type_from_tags(tags, item_type): return [ item for item in map( parse_tag, tags - ) if isinstance(item, type) + ) if isinstance(item, item_type) ] - def allure_labels(tags): return labels_set( get_items_of_type_from_tags(tags, Label) diff --git a/pyproject.toml b/pyproject.toml index e5e8a84e..54080412 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,7 @@ +[tool.poe.tasks] +linter = "flake8 ./allure-*/src ./tests" +tests = "pytest" + [tool.pytest.ini_options] testpaths = [ "tests" @@ -5,4 +9,4 @@ testpaths = [ addopts = [ "-p", "no:allure_pytest", "-p", "no:allure_pytest_bdd" -] \ No newline at end of file +] From f60862e0ea328ab36c2680d1fa50ccc60c9ac306 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 24 Feb 2023 01:26:32 +0700 Subject: [PATCH 10/18] Reorganize deps into multiple requirement files --- allure-behave/requirements.txt | 6 +++--- allure-nose2/requirements.txt | 7 +++---- allure-pytest-bdd/requirements.txt | 8 +++----- allure-pytest/requirements.txt | 15 +++----------- allure-python-commons-test/requirements.txt | 8 ++------ allure-python-commons/requirements.txt | 9 ++------- allure-robotframework/requirements.txt | 11 +++------- requirements/commons.txt | 2 ++ requirements/core.txt | 2 ++ requirements/dev.txt | 20 +++++++++++++++++++ requirements/linting.txt | 3 +++ requirements/testing.txt | 7 +++++++ requirements/testing/allure-behave.txt | 1 + requirements/testing/allure-nose2.txt | 1 + requirements/testing/allure-pytest-bdd.txt | 1 + requirements/testing/allure-pytest.txt | 6 ++++++ .../testing/allure-python-commons-test.txt | 1 + .../testing/allure-python-commons.txt | 1 + .../testing/allure-robotframework.txt | 2 ++ 19 files changed, 66 insertions(+), 45 deletions(-) create mode 100644 requirements/commons.txt create mode 100644 requirements/core.txt create mode 100644 requirements/dev.txt create mode 100644 requirements/linting.txt create mode 100644 requirements/testing.txt create mode 100644 requirements/testing/allure-behave.txt create mode 100644 requirements/testing/allure-nose2.txt create mode 100644 requirements/testing/allure-pytest-bdd.txt create mode 100644 requirements/testing/allure-pytest.txt create mode 100644 requirements/testing/allure-python-commons-test.txt create mode 100644 requirements/testing/allure-python-commons.txt create mode 100644 requirements/testing/allure-robotframework.txt diff --git a/allure-behave/requirements.txt b/allure-behave/requirements.txt index a990291a..f946db6e 100644 --- a/allure-behave/requirements.txt +++ b/allure-behave/requirements.txt @@ -1,3 +1,3 @@ -poethepoet -# linters -flake8 +-r ../requirements/testing.txt +-r ../requirements/linting.txt +-r ../requirements/testing/allure-behave.txt diff --git a/allure-nose2/requirements.txt b/allure-nose2/requirements.txt index b7bb2e3a..6bc47501 100644 --- a/allure-nose2/requirements.txt +++ b/allure-nose2/requirements.txt @@ -1,4 +1,3 @@ -poethepoet -# linters -flake8 -flake8-builtins \ No newline at end of file +-r ../requirements/testing.txt +-r ../requirements/linting.txt +-r ../requirements/testing/allure-nose2.txt diff --git a/allure-pytest-bdd/requirements.txt b/allure-pytest-bdd/requirements.txt index 0dffc5ce..bb3a6759 100644 --- a/allure-pytest-bdd/requirements.txt +++ b/allure-pytest-bdd/requirements.txt @@ -1,5 +1,3 @@ -mock -poethepoet -# linters -flake8 -flake8-builtins \ No newline at end of file +-r ../requirements/testing.txt +-r ../requirements/linting.txt +-r ../requirements/testing/allure-pytest-bdd.txt diff --git a/allure-pytest/requirements.txt b/allure-pytest/requirements.txt index 70595068..bd3fab25 100644 --- a/allure-pytest/requirements.txt +++ b/allure-pytest/requirements.txt @@ -1,12 +1,3 @@ -# test requirements -pyhamcrest -mock -pytest-check -pytest-flakes -pytest-rerunfailures -pytest-xdist -pytest-lazy-fixture -poethepoet -# linters -flake8 -flake8-builtins \ No newline at end of file +-r ../requirements/testing.txt +-r ../requirements/linting.txt +-r ../requirements/testing/allure-pytest.txt diff --git a/allure-python-commons-test/requirements.txt b/allure-python-commons-test/requirements.txt index ef1325ef..ca6d5b91 100644 --- a/allure-python-commons-test/requirements.txt +++ b/allure-python-commons-test/requirements.txt @@ -1,6 +1,2 @@ -# test requrements -pyhamcrest -poethepoet -# linters -flake8 -flake8-builtins \ No newline at end of file +-r ../requirements/testing.txt +-r ../requirements/linting.txt diff --git a/allure-python-commons/requirements.txt b/allure-python-commons/requirements.txt index 2f971461..ca6d5b91 100644 --- a/allure-python-commons/requirements.txt +++ b/allure-python-commons/requirements.txt @@ -1,7 +1,2 @@ -# test requrements -attr -pyhamcrest -pluggy -poethepoet -# linters -flake8 +-r ../requirements/testing.txt +-r ../requirements/linting.txt diff --git a/allure-robotframework/requirements.txt b/allure-robotframework/requirements.txt index 999317b6..b2e49b0f 100644 --- a/allure-robotframework/requirements.txt +++ b/allure-robotframework/requirements.txt @@ -1,8 +1,3 @@ -# test requrements -robotframework-pabot -docutils -pyhamcrest -poethepoet -# linters -flake8 -flake8-builtins +-r ../requirements/testing.txt +-r ../requirements/linting.txt +-r ../requirements/testing/allure-robotframework.txt diff --git a/requirements/commons.txt b/requirements/commons.txt new file mode 100644 index 00000000..eedfd669 --- /dev/null +++ b/requirements/commons.txt @@ -0,0 +1,2 @@ +-e allure-python-commons +-e allure-python-commons-test diff --git a/requirements/core.txt b/requirements/core.txt new file mode 100644 index 00000000..af8ea1ae --- /dev/null +++ b/requirements/core.txt @@ -0,0 +1,2 @@ +# Task runner +poethepoet diff --git a/requirements/dev.txt b/requirements/dev.txt new file mode 100644 index 00000000..2c5fe0a7 --- /dev/null +++ b/requirements/dev.txt @@ -0,0 +1,20 @@ +# Commons: +-r ./commons.txt + +# Allure integrations +-e allure-behave +-e allure-nose2 +-e allure-pytest +-e allure-pytest-bdd +-e allure-robotframework + +# Testing & linting +-r ./testing.txt +-r ./linting.txt + +# Allure per-package testing deps +-r ./testing/allure-behave.txt +-r ./testing/allure-nose2.txt +-r ./testing/allure-pytest.txt +-r ./testing/allure-pytest-bdd.txt +-r ./testing/allure-robotframework.txt diff --git a/requirements/linting.txt b/requirements/linting.txt new file mode 100644 index 00000000..fc08386d --- /dev/null +++ b/requirements/linting.txt @@ -0,0 +1,3 @@ +-r ./core.txt +flake8 +flake8-builtins diff --git a/requirements/testing.txt b/requirements/testing.txt new file mode 100644 index 00000000..09919cdf --- /dev/null +++ b/requirements/testing.txt @@ -0,0 +1,7 @@ +-r ./core.txt +docutils +mock +poethepoet +PyHamcrest +Pygments +pytest diff --git a/requirements/testing/allure-behave.txt b/requirements/testing/allure-behave.txt new file mode 100644 index 00000000..0de2f9a3 --- /dev/null +++ b/requirements/testing/allure-behave.txt @@ -0,0 +1 @@ +# allure_behave testing deps diff --git a/requirements/testing/allure-nose2.txt b/requirements/testing/allure-nose2.txt new file mode 100644 index 00000000..22c30d31 --- /dev/null +++ b/requirements/testing/allure-nose2.txt @@ -0,0 +1 @@ +# allure_nose2 testing deps diff --git a/requirements/testing/allure-pytest-bdd.txt b/requirements/testing/allure-pytest-bdd.txt new file mode 100644 index 00000000..19b9ce8f --- /dev/null +++ b/requirements/testing/allure-pytest-bdd.txt @@ -0,0 +1 @@ +# allure_pytest_bdd testing deps diff --git a/requirements/testing/allure-pytest.txt b/requirements/testing/allure-pytest.txt new file mode 100644 index 00000000..ca239ac7 --- /dev/null +++ b/requirements/testing/allure-pytest.txt @@ -0,0 +1,6 @@ +# allure_pytest testing deps +pytest-check +pytest-flakes +pytest-rerunfailures +pytest-xdist +pytest-lazy-fixture diff --git a/requirements/testing/allure-python-commons-test.txt b/requirements/testing/allure-python-commons-test.txt new file mode 100644 index 00000000..ff36d34a --- /dev/null +++ b/requirements/testing/allure-python-commons-test.txt @@ -0,0 +1 @@ +# allure_commons_test testing deps diff --git a/requirements/testing/allure-python-commons.txt b/requirements/testing/allure-python-commons.txt new file mode 100644 index 00000000..84ed3139 --- /dev/null +++ b/requirements/testing/allure-python-commons.txt @@ -0,0 +1 @@ +# allure_commons testing deps diff --git a/requirements/testing/allure-robotframework.txt b/requirements/testing/allure-robotframework.txt new file mode 100644 index 00000000..842589fe --- /dev/null +++ b/requirements/testing/allure-robotframework.txt @@ -0,0 +1,2 @@ +# allure_robotframework testing deps +robotframework-pabot From b8f7a999a8a9e00d872873764a794274ba8627a5 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 24 Feb 2023 01:29:04 +0700 Subject: [PATCH 11/18] Fix workflow to match new test layout - add test folders to change collection filters - change the linting job to checks all the code base at once - rename the build job to test - remove unnecessary deps installation from lint & test jobs --- .github/workflows/build.yaml | 34 ++++++++++++++++------------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 93ae5b14..18ff79ad 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -20,22 +20,32 @@ jobs: - allure-behave/** - allure-python-commons/** - allure-python-commons-test/** + - tests/*.py + - tests/allure_behave/** allure-nose2: - allure-nose2/** - allure-python-commons/** - allure-python-commons-test/** + - tests/*.py + - tests/allure_nose2/** allure-pytest: - allure-pytest/** - allure-python-commons/** - allure-python-commons-test/** + - tests/*.py + - tests/allure_pytest/** allure-pytest-bdd: - allure-pytest-bdd/** - allure-python-commons/** - allure-python-commons-test/** + - tests/*.py + - tests/allure_pytest_bdd/** allure-robotframework: - allure-robotframework/** - allure-python-commons/** - allure-python-commons-test/** + - tests/*.py + - tests/allure_robotframework/** allure-python-commons: allure-python-commons/** allure-python-commons-test: allure-python-commons-test/** @@ -63,9 +73,6 @@ jobs: runs-on: ubuntu-latest needs: [commons, changes] if: ${{ needs.changes.outputs.packages != '[]' }} - strategy: - matrix: - package: ${{ fromJSON(needs.changes.outputs.packages) }} steps: - uses: actions/checkout@v3 @@ -74,24 +81,14 @@ jobs: with: python-version: "3.11" - - name: Get commons from cache - id: commons - uses: actions/cache@v3 - with: - path: dist/ - key: commons-${{ github.sha }} - - - name: Install packages - run: pip install dist/allure-python-commons*.tar.gz && - pip install ./${{ matrix.package }} && - pip install -r ${{ matrix.package }}/requirements.txt + - name: Install linting packages + run: pip install -r ./requirements/linting.txt - - name: Static check ${{ matrix.package }} - working-directory: ${{ matrix.package }} + - name: Linting the codebase run: poe linter build: - name: Build package + name: Test package runs-on: ubuntu-latest needs: [linters, commons, changes] if: ${{ needs.changes.outputs.packages != '[]' }} @@ -120,7 +117,8 @@ jobs: - name: Install packages run: pip install dist/allure-python-commons*.tar.gz && pip install ./${{ matrix.package }} && - pip install -r ${{ matrix.package }}/requirements.txt + pip install ./requirements/testing.txt && + pip install -r ./requirements/testing/${{ matrix.package }}.txt - name: Test ${{ matrix.package }} working-directory: ${{ matrix.package }} From 6599e287d21a3e65687479cbbc850490c9f1779e Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 24 Feb 2023 01:42:53 +0700 Subject: [PATCH 12/18] Fix lint & test workflow: -r missing in pip install --- .github/workflows/build.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yaml b/.github/workflows/build.yaml index 18ff79ad..458db329 100644 --- a/.github/workflows/build.yaml +++ b/.github/workflows/build.yaml @@ -117,7 +117,7 @@ jobs: - name: Install packages run: pip install dist/allure-python-commons*.tar.gz && pip install ./${{ matrix.package }} && - pip install ./requirements/testing.txt && + pip install -r ./requirements/testing.txt && pip install -r ./requirements/testing/${{ matrix.package }}.txt - name: Test ${{ matrix.package }} From b4a02a7c55e22136fb0087ca2f7016c18652537a Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 24 Feb 2023 01:56:58 +0700 Subject: [PATCH 13/18] Replace | operator with Union in typings for py<3.10 compat --- tests/allure_behave/behave_runner.py | 10 +++++----- tests/allure_pytest/pytest_runner.py | 10 +++++----- tests/allure_robotframework/robot_runner.py | 8 ++++---- tests/e2e.py | 19 +++++++++++-------- 4 files changed, 25 insertions(+), 22 deletions(-) diff --git a/tests/allure_behave/behave_runner.py b/tests/allure_behave/behave_runner.py index bb126916..5eed2de2 100644 --- a/tests/allure_behave/behave_runner.py +++ b/tests/allure_behave/behave_runner.py @@ -12,7 +12,7 @@ from pathlib import Path from pytest import FixtureRequest, Pytester from typing import Sequence -from tests.e2e import AllureFrameworkRunner +from tests.e2e import AllureFrameworkRunner, PathlikeT from allure_behave.formatter import AllureFormatter @@ -118,17 +118,17 @@ def __init__(self, request: FixtureRequest, pytester: Pytester): def run_behave( self, - feature_paths: Sequence[str | Path] = None, + feature_paths: Sequence[PathlikeT] = None, feature_literals: Sequence[str] = None, feature_rst_ids: Sequence[str] = None, - step_paths: Sequence[str | Path] = None, + step_paths: Sequence[PathlikeT] = None, step_literals: Sequence[str] = None, step_rst_ids: Sequence[str] = None, - environment_path: str | Path = None, + environment_path: PathlikeT = None, environment_literal: str = None, environment_rst_id: str = None, testplan_content: dict = None, - testplan_path: str | Path = None, + testplan_path: PathlikeT = None, testplan_rst_id: str = None, options: Sequence[str] = None, ): diff --git a/tests/allure_pytest/pytest_runner.py b/tests/allure_pytest/pytest_runner.py index 6637d519..c62dfe31 100644 --- a/tests/allure_pytest/pytest_runner.py +++ b/tests/allure_pytest/pytest_runner.py @@ -1,8 +1,8 @@ from doctest import script_from_examples from pathlib import Path from pytest import FixtureRequest, Pytester, StashKey -from tests.e2e import AllureFrameworkRunner, altered_env -from typing import Sequence, Tuple +from tests.e2e import AllureFrameworkRunner, altered_env, PathlikeT +from typing import Sequence, Tuple, Union from allure_commons.logger import AllureMemoryLogger @@ -47,7 +47,7 @@ def select_plugins(self, *plugins: str) -> None: def run_docstring( self, *cli_args: str, - filename: str | Path = None, + filename: PathlikeT = None, testplan: dict = None ) -> AllureMemoryLogger: """Runs a doctest from a docstring of the current test node (or one of its @@ -76,7 +76,7 @@ def run_docstring( def run_docpath_examples( self, *cli_args: str, - filename: str | Path = None, + filename: PathlikeT = None, testplan: dict = None, cache: bool = False ) -> AllureMemoryLogger: @@ -122,7 +122,7 @@ def run_docpath_examples( def run_pytest( self, - *testfile_literals: str | Tuple[str | Path, str], + *testfile_literals: Union[str, Tuple[PathlikeT, str]], conftest_literal: str = None, cli_args: Sequence[str] = None, testplan: dict = None diff --git a/tests/allure_robotframework/robot_runner.py b/tests/allure_robotframework/robot_runner.py index c66fc92d..024c9ab9 100644 --- a/tests/allure_robotframework/robot_runner.py +++ b/tests/allure_robotframework/robot_runner.py @@ -1,7 +1,7 @@ import robot from pathlib import Path from pytest import FixtureRequest, Pytester -from tests.e2e import AllureFrameworkRunner +from tests.e2e import AllureFrameworkRunner, PathlikeT from typing import Sequence, Mapping from allure_robotframework import allure_robotframework @@ -16,14 +16,14 @@ def __init__(self, request: FixtureRequest, pytester: Pytester): def run_robotframework( self, - suite_paths: Sequence[str | Path] = None, + suite_paths: Sequence[PathlikeT] = None, suite_literals: Mapping[str, str] = None, suite_rst_ids: Sequence[str] = None, - library_paths: Sequence[str | Path] = None, + library_paths: Sequence[PathlikeT] = None, library_literals: Mapping[str, str] = None, library_rst_ids: Sequence[str] = None, testplan_content: dict = None, - testplan_path: str | Path = None, + testplan_path: PathlikeT = None, testplan_rst_id: str = None, options: Mapping[str, any] = None ) -> None: diff --git a/tests/e2e.py b/tests/e2e.py index 3b1cfdd0..4d635e72 100644 --- a/tests/e2e.py +++ b/tests/e2e.py @@ -15,13 +15,16 @@ from contextlib import contextmanager from pathlib import Path from pytest import FixtureRequest, Pytester, MonkeyPatch -from typing import Tuple, Mapping, TypeVar, Generator, Callable +from typing import Tuple, Mapping, TypeVar, Generator, Callable, Union import allure_commons from allure_commons.logger import AllureMemoryLogger from allure_commons_test.report import AllureReport +PathlikeT = Union[str, Path] + + @contextmanager def altered_env(**kwargs) -> Generator[MonkeyPatch, None, None]: mp = MonkeyPatch() @@ -98,7 +101,7 @@ def __init__(self): @contextmanager def allure_file_context( - alluredir: str | Path + alluredir: PathlikeT ) -> Generator[AllureFileContextValue, None, None]: """Creates a context to test an allure integration. @@ -128,7 +131,7 @@ def allure_file_context( def find_node_with_docstring( request: FixtureRequest -) -> Tuple[pytest.Item, str] | Tuple[None, None]: +) -> Union[Tuple[pytest.Item, str], Tuple[None, None]]: """Find a docstring associated with a test function. It first checks the function itself and then, if no docstring was found, @@ -209,7 +212,7 @@ class RstExampleTable: STASH_KEY = pytest.StashKey() - def __init__(self, filepath: str | Path) -> None: + def __init__(self, filepath: PathlikeT) -> None: """Create a table of examples from a .rst document. Arguments: @@ -277,7 +280,7 @@ def find_examples(request: FixtureRequest) -> RstExampleTableT: return examples @staticmethod - def __load_examples(filepath: str | Path) -> Mapping[str, str]: + def __load_examples(filepath: PathlikeT) -> Mapping[str, str]: document = RstExampleTable.parse_rst(filepath) return { name: code_block.astext() @@ -287,7 +290,7 @@ def __load_examples(filepath: str | Path) -> Mapping[str, str]: } @staticmethod - def parse_rst(filepath: str | Path) -> docutils.nodes.document: + def parse_rst(filepath: PathlikeT) -> docutils.nodes.document: with warnings.catch_warnings(): warnings.simplefilter("ignore", DeprecationWarning) parser = docutils.parsers.rst.Parser() @@ -344,7 +347,7 @@ def _run( self, *args, testplan_content: dict = None, - testplan_path: str | Path = None, + testplan_path: PathlikeT = None, testplan_rst_id: str = None, logger_path: str = None, **kwargs @@ -521,7 +524,7 @@ def _make_files_from_rst( ) for name, rst_id in (rst_name_to_id or {}).items() ] - def _read_file(self, path: str | Path, basepath: str | Path = None): + def _read_file(self, path: PathlikeT, basepath: PathlikeT = None): """Read file content. Arguments: From c55ed11bb3095e357a60de81aa883060953f8b27 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 24 Feb 2023 01:59:06 +0700 Subject: [PATCH 14/18] Fix linting errors --- tests/allure_behave/behave_runner.py | 1 - tests/allure_robotframework/robot_runner.py | 1 - 2 files changed, 2 deletions(-) diff --git a/tests/allure_behave/behave_runner.py b/tests/allure_behave/behave_runner.py index 5eed2de2..34a3561f 100644 --- a/tests/allure_behave/behave_runner.py +++ b/tests/allure_behave/behave_runner.py @@ -9,7 +9,6 @@ from behave.runner import Context, Runner from behave.step_registry import setup_step_decorators from behave.step_registry import StepRegistry -from pathlib import Path from pytest import FixtureRequest, Pytester from typing import Sequence from tests.e2e import AllureFrameworkRunner, PathlikeT diff --git a/tests/allure_robotframework/robot_runner.py b/tests/allure_robotframework/robot_runner.py index 024c9ab9..c601f4a4 100644 --- a/tests/allure_robotframework/robot_runner.py +++ b/tests/allure_robotframework/robot_runner.py @@ -1,5 +1,4 @@ import robot -from pathlib import Path from pytest import FixtureRequest, Pytester from tests.e2e import AllureFrameworkRunner, PathlikeT from typing import Sequence, Mapping From ac0f2525bf73cfe8028ce3c581ffb4b5541a0004 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 24 Feb 2023 18:39:20 +0700 Subject: [PATCH 15/18] Refactor try-catch logic into check-return --- allure-pytest/src/utils.py | 5 +---- allure-python-commons/src/logger.py | 31 +++++++++++++---------------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/allure-pytest/src/utils.py b/allure-pytest/src/utils.py index cc59e244..56f12157 100644 --- a/allure-pytest/src/utils.py +++ b/allure-pytest/src/utils.py @@ -27,10 +27,7 @@ def get_marker_value(item, keyword): def allure_title(item): - try: - return item._obj.__allure_display_name__ - except AttributeError: - return None + return getattr(item.obj, "__allure_display_name__", None) def allure_description(item): diff --git a/allure-python-commons/src/logger.py b/allure-python-commons/src/logger.py index 2fed288e..c3a69299 100644 --- a/allure-python-commons/src/logger.py +++ b/allure-python-commons/src/logger.py @@ -1,6 +1,6 @@ -import errno import io import os +from pathlib import Path import json import uuid import shutil @@ -13,24 +13,21 @@ class AllureFileLogger: def __init__(self, report_dir, clean=False): - self._report_dir = report_dir - - try: - os.makedirs(report_dir) - except OSError as e: - if e.errno != errno.EEXIST: - raise - elif clean: - for f in os.listdir(report_dir): - f = os.path.join(report_dir, f) - if os.path.isfile(f): - os.unlink(f) + self._report_dir = Path(report_dir).absolute() + if self._report_dir.is_dir() and clean: + shutil.rmtree(self._report_dir) + self._report_dir.mkdir(parents=True, exist_ok=True) def _report_item(self, item): indent = INDENT if os.environ.get("ALLURE_INDENT_OUTPUT") else None filename = item.file_pattern.format(prefix=uuid.uuid4()) - data = asdict(item, filter=lambda attr, value: not (type(value) != bool and not bool(value))) - with io.open(os.path.join(self._report_dir, filename), 'w', encoding='utf8') as json_file: + data = asdict( + item, + filter=lambda attr, value: not ( + type(value) != bool and not bool(value) + ) + ) + with io.open(self._report_dir / filename, 'w', encoding='utf8') as json_file: json.dump(data, json_file, indent=indent, ensure_ascii=False) @hookimpl @@ -43,12 +40,12 @@ def report_container(self, container): @hookimpl def report_attached_file(self, source, file_name): - destination = os.path.join(self._report_dir, file_name) + destination = self._report_dir / file_name shutil.copy2(source, destination) @hookimpl def report_attached_data(self, body, file_name): - destination = os.path.join(self._report_dir, file_name) + destination = self._report_dir / file_name with open(destination, 'wb') as attached_file: if isinstance(body, str): attached_file.write(body.encode('utf-8')) From 8f33eb0d3129250bbfbb47c5780474c30d2f7004 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 24 Feb 2023 18:40:38 +0700 Subject: [PATCH 16/18] Fix testing with allure enabled; ignore allure output --- .gitignore | 5 ++++- tests/allure_pytest/pytest_runner.py | 5 ++++- tests/allure_pytest_bdd/conftest.py | 1 + tests/e2e.py | 2 +- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index af0bb13b..523fc340 100644 --- a/.gitignore +++ b/.gitignore @@ -8,4 +8,7 @@ *.egg-info */build -*/dist \ No newline at end of file +*/dist + +/.allure-report +/.allure-results diff --git a/tests/allure_pytest/pytest_runner.py b/tests/allure_pytest/pytest_runner.py index c62dfe31..334b09dc 100644 --- a/tests/allure_pytest/pytest_runner.py +++ b/tests/allure_pytest/pytest_runner.py @@ -18,9 +18,11 @@ class AllurePytestRunner(AllureFrameworkRunner): """ + LOGGER_PATH = "allure_pytest.plugin.AllureFileLogger" DOCTEST_RESULT_KEY = StashKey() def __init__(self, request: FixtureRequest, pytester: Pytester): + self.logger_path = AllurePytestRunner.LOGGER_PATH super().__init__(request, pytester) self.select_plugins("allure_pytest") @@ -159,7 +161,8 @@ def run_pytest( self.__generate_testfiles(testfile_literals) return self._run( pytest_args, - testplan_content=testplan + testplan_content=testplan, + logger_path=self.logger_path ) def _run_framework(self, options): diff --git a/tests/allure_pytest_bdd/conftest.py b/tests/allure_pytest_bdd/conftest.py index 2252e93b..ba247cab 100644 --- a/tests/allure_pytest_bdd/conftest.py +++ b/tests/allure_pytest_bdd/conftest.py @@ -5,5 +5,6 @@ @pytest.fixture def allure_pytest_bdd_runner(request, pytester): runner = AllurePytestRunner(request, pytester) + runner.logger_path = "allure_pytest_bdd.plugin.AllureFileLogger" runner.select_plugins("pytest-bdd", "allure_pytest_bdd") yield runner diff --git a/tests/e2e.py b/tests/e2e.py index 4d635e72..cce35ee7 100644 --- a/tests/e2e.py +++ b/tests/e2e.py @@ -614,5 +614,5 @@ def __pop_all_allure_plugins(): def __restore_allure_plugins(name_plugin_tuples): - for plugin, name in name_plugin_tuples: + for name, plugin in name_plugin_tuples: allure_commons.plugin_manager.register(plugin, name) From 832e1eac8f131a44f36c6dcfc8f67fca68400e74 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Fri, 24 Feb 2023 18:41:03 +0700 Subject: [PATCH 17/18] Add allure report tasks --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 54080412..5485c029 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,9 @@ [tool.poe.tasks] linter = "flake8 ./allure-*/src ./tests" tests = "pytest" +allure-collect = "pytest -p allure_pytest --alluredir ./.allure-results --clean-alluredir --allure-link-pattern issue:https://github.com/allure-framework/allure-python/issues/{0}" +allure-generate = "allure generate --clean --output ./.allure-report ./.allure-results" +allure-open = "allure open ./.allure-report" [tool.pytest.ini_options] testpaths = [ From de3b3761a199d89b505d6c8e6a39f2eb82c70d81 Mon Sep 17 00:00:00 2001 From: Maksim Stepanov <17935127+delatrie@users.noreply.github.com> Date: Tue, 28 Feb 2023 01:50:50 +0700 Subject: [PATCH 18/18] Fix typo in allure-behave example. Make all req file name more expressive --- allure-behave/examples/label.rst | 4 ++-- requirements/{dev.txt => all.txt} | 0 2 files changed, 2 insertions(+), 2 deletions(-) rename requirements/{dev.txt => all.txt} (100%) diff --git a/allure-behave/examples/label.rst b/allure-behave/examples/label.rst index 526ee5ff..0dcb314f 100644 --- a/allure-behave/examples/label.rst +++ b/allure-behave/examples/label.rst @@ -13,5 +13,5 @@ It's possible to add a custom label to a behave scenario. Simply apply Scenario: Scenario marked with an author label Given noop -Note, that neither the name nor the value of a label, added that way, cannot -contain whitespaces. +Note, that neither the name nor the value of a label, added that way, may +contain a whitespace character. diff --git a/requirements/dev.txt b/requirements/all.txt similarity index 100% rename from requirements/dev.txt rename to requirements/all.txt