From e8d46f8fe5e55ea65831a606297a6a37ab6988b6 Mon Sep 17 00:00:00 2001 From: Raz Amir Date: Wed, 21 May 2025 14:47:18 +0300 Subject: [PATCH 1/2] Add an option to ignore param value in test case id --- pytest_reportportal/service.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/pytest_reportportal/service.py b/pytest_reportportal/service.py index 2d42661..b0ebe1b 100644 --- a/pytest_reportportal/service.py +++ b/pytest_reportportal/service.py @@ -599,18 +599,26 @@ def _get_code_ref(self, item: Item) -> str: def _get_test_case_id(self, mark, leaf: Dict[str, Any]) -> str: parameters: Optional[Dict[str, Any]] = leaf.get("parameters", None) + parameters_indices: Optional[Dict[str, Any]] = leaf.get("parameters_indices") or {} parameterized = True selected_params: Optional[List[str]] = None + use_index = False if mark is not None: parameterized = mark.kwargs.get("parameterized", False) selected_params: Optional[Union[str, List[str]]] = mark.kwargs.get("params", None) + use_index = mark.kwargs.get("use_index", False) if selected_params is not None and not isinstance(selected_params, list): selected_params = [selected_params] param_str = None if parameterized and parameters is not None and len(parameters) > 0: if selected_params is not None and len(selected_params) > 0: - param_list = [str(parameters.get(param, None)) for param in selected_params] + if use_index: + param_list = [str((param, parameters_indices.get(param, None))) for param in selected_params] + else: + param_list = [str(parameters.get(param, None)) for param in selected_params] + elif use_index: + param_list = [str(param) for param in parameters_indices.items()] else: param_list = [str(param) for param in parameters.values()] param_str = "[{}]".format(",".join(sorted(param_list))) @@ -729,6 +737,19 @@ def _get_parameters(self, item) -> Optional[Dict[str, Any]]: return None return {str(k): v.replace("\0", "\\0") if isinstance(v, str) else v for k, v in params.items()} + def _get_parameters_indices(self, item) -> Optional[Dict[str, Any]]: + """ + Get params indices of item. + + :param item: Pytest.Item + :return: dict of params indices + """ + indices = item.callspec.indices if hasattr(item, "callspec") else None + if not indices: + return None + + return indices + def _process_test_case_id(self, leaf: Dict[str, Any]) -> str: """ Process Test Case ID if set. @@ -793,6 +814,7 @@ def _process_metadata_item_start(self, leaf: Dict[str, Any]) -> None: leaf["name"] = self._process_item_name(leaf) leaf["description"] = self._get_item_description(item) leaf["parameters"] = self._get_parameters(item) + leaf["parameters_indices"] = self._get_parameters_indices(item) leaf["code_ref"] = self._get_code_ref(item) leaf["test_case_id"] = self._process_test_case_id(leaf) leaf["issue"] = self._process_issue(item) From fb7c883b17640c67828ead1b66b72006a62d2276 Mon Sep 17 00:00:00 2001 From: Raz Amir Date: Thu, 12 Jun 2025 09:31:33 +0300 Subject: [PATCH 2/2] Add test cases --- .../test_case_id_decorator_use_index_false.py | 24 ++++++ ...e_id_decorator_use_index_partial_params.py | 24 ++++++ .../test_case_id_decorator_use_index_true.py | 24 ++++++ .../test_case_id_use_index_report.py | 77 +++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 examples/test_case_id/test_case_id_decorator_use_index_false.py create mode 100644 examples/test_case_id/test_case_id_decorator_use_index_partial_params.py create mode 100644 examples/test_case_id/test_case_id_decorator_use_index_true.py create mode 100644 tests/integration/test_case_id_use_index_report.py diff --git a/examples/test_case_id/test_case_id_decorator_use_index_false.py b/examples/test_case_id/test_case_id_decorator_use_index_false.py new file mode 100644 index 0000000..e685e4a --- /dev/null +++ b/examples/test_case_id/test_case_id_decorator_use_index_false.py @@ -0,0 +1,24 @@ +"""A simple example test with Test Case ID decorator using parameter values.""" + +# Copyright (c) 2023 https://reportportal.io . +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +import pytest + +TEST_CASE_ID = "ISSUE-USE-INDEX-FALSE" + + +@pytest.mark.parametrize(("param1", "param2"), [("value1", "value2")]) +@pytest.mark.tc_id(TEST_CASE_ID, parameterized=True, use_index=False) +def test_case_id_decorator(param1, param2): + assert True diff --git a/examples/test_case_id/test_case_id_decorator_use_index_partial_params.py b/examples/test_case_id/test_case_id_decorator_use_index_partial_params.py new file mode 100644 index 0000000..c130f2e --- /dev/null +++ b/examples/test_case_id/test_case_id_decorator_use_index_partial_params.py @@ -0,0 +1,24 @@ +"""A simple example test with Test Case ID decorator using parameter indices with selected params.""" + +# Copyright (c) 2023 https://reportportal.io . +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +import pytest + +TEST_CASE_ID = "ISSUE-USE-INDEX-PARTIAL" + + +@pytest.mark.parametrize(("param1", "param2"), [("value1", "value2")]) +@pytest.mark.tc_id(TEST_CASE_ID, parameterized=True, params=["param1"], use_index=True) +def test_case_id_decorator(param1, param2): + assert True diff --git a/examples/test_case_id/test_case_id_decorator_use_index_true.py b/examples/test_case_id/test_case_id_decorator_use_index_true.py new file mode 100644 index 0000000..7b8bf1b --- /dev/null +++ b/examples/test_case_id/test_case_id_decorator_use_index_true.py @@ -0,0 +1,24 @@ +"""A simple example test with Test Case ID decorator using parameter indices.""" + +# Copyright (c) 2023 https://reportportal.io . +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +import pytest + +TEST_CASE_ID = "ISSUE-USE-INDEX-TRUE" + + +@pytest.mark.parametrize(("param1", "param2"), [("value1", "value2")]) +@pytest.mark.tc_id(TEST_CASE_ID, parameterized=True, use_index=True) +def test_case_id_decorator(param1, param2): + assert True diff --git a/tests/integration/test_case_id_use_index_report.py b/tests/integration/test_case_id_use_index_report.py new file mode 100644 index 0000000..5e6a359 --- /dev/null +++ b/tests/integration/test_case_id_use_index_report.py @@ -0,0 +1,77 @@ +# Copyright (c) 2023 https://reportportal.io . +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License + +"""This module includes integration tests for Test Case ID report with use_index parameter.""" + +from unittest import mock + +import pytest + +from examples.test_case_id import ( + test_case_id_decorator_use_index_false, + test_case_id_decorator_use_index_partial_params, + test_case_id_decorator_use_index_true, +) +from tests import REPORT_PORTAL_SERVICE +from tests.helpers import utils + + +@mock.patch(REPORT_PORTAL_SERVICE) +@pytest.mark.parametrize( + ["test", "expected_id"], + [ + # Test use_index=True: should use parameter indices instead of values + # Format should be: ('param1', 0),('param2', 0) instead of value1,value2 + ( + "examples/test_case_id/test_case_id_decorator_use_index_true.py", + test_case_id_decorator_use_index_true.TEST_CASE_ID + "[('param1', 0),('param2', 0)]", + ), + # Test use_index=False: should use parameter values (default behavior) + ( + "examples/test_case_id/test_case_id_decorator_use_index_false.py", + test_case_id_decorator_use_index_false.TEST_CASE_ID + "[value1,value2]", + ), + # Test use_index=True with selected params: should use index for selected param only + # Should be ('param1', 0) instead of value1 + ( + "examples/test_case_id/test_case_id_decorator_use_index_partial_params.py", + test_case_id_decorator_use_index_partial_params.TEST_CASE_ID + "[('param1', 0)]", + ), + ], +) +def test_use_index_parameters(mock_client_init, test, expected_id): + """Verify the use_index parameter in Test Case ID functionality. + + This test verifies that the new use_index parameter works correctly for parameterized tests. + When use_index=True, the test case ID should include parameter indices instead of parameter values. + This is useful for scenarios where parameter values might change between test runs but the indices + remain constant, ensuring that retry groups are properly maintained. + + The test covers: + 1. use_index=True with all parameters - should use (param_name, index) format + 2. use_index=False - should use parameter values (default behavior) + 3. use_index=True with selected parameters - should use indices only for selected params + + :param mock_client_init: Pytest fixture for mocking the ReportPortal client + :param test: a test file path to run + :param expected_id: the expected Test Case ID format + """ + result = utils.run_pytest_tests(tests=[test]) + assert int(result) == 0, "Exit code should be 0 (no errors)" + + mock_client = mock_client_init.return_value + assert mock_client.start_test_item.call_count > 0, '"start_test_item" called incorrect number of times' + + call_args = mock_client.start_test_item.call_args_list + step_call_args = call_args[-1][1] + assert step_call_args["test_case_id"] == expected_id