# -*- coding: utf-8 -*- # This program is free software; you can redistribute it and/or modify it under # the terms of the (LGPL) GNU Lesser General Public License as published by the # Free Software Foundation; either version 3 of the License, or (at your # option) any later version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU Library Lesser General Public License # for more details at ( http://www.gnu.org/licenses/lgpl.html ). # # You should have received a copy of the GNU Lesser General Public License # along with this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # written by: Jurko Gospodnetić ( jurko.gospodnetic@pke.hr ) """ Indirect parametrization pytest plugin. Allows tests parametrization data to be generated using a function instead of having to be explicitly specified as is the case when using the builtin pytest.mark.parametrize functionality. Interface is similar to the builtin pytest.mark.parametrize implementation, except that it takes an indirect parametrization function as an additional initial positional argument. All the other arguments parameters are forwarded onto this indirect parametrization function which then calculates and returns the actual parametrization data. The return values are a standard Python positional argument list and keyword argument dictionary to be used for the underlying metafunc.parametrize() call. May be used as either: 1. pytest.indirect_parametrizer() or 2. pytest.mark.indirect_parametrizer(). The two usages are equivalent except that the first one may be used with any indirect parametrization function while the second one can not be used with indirect parametrization functions taking no input parameters (with pytest versions prior to 2.5.2 it also can not be used with functions taking only keyword arguments). This is a technical restriction based on an underlying pytest.mark implementation detail. See the indirect_parametrize() function doc-string for more detailed information. Usage example making the following test_example() test function calls after calculating them from a much shorter definition: * test_example("Fritula", 1) * test_example("Fritula", 2) * test_example("Fritula", 3) * test_example("Fritula", 4) * test_example("Fritula", 5) * test_example("Fritula", 6) * test_example("Fritula", 7) * test_example("Fritula", 8) * test_example("Fritula", 9) * test_example("Madagascar", 10) * test_example("Madagascar", 20) * test_example("Madagascar", 30) * test_example("Rumpelstiltskin", 20) * test_example("Rumpelstiltskin", 40) def custom_parametrization(param_names, param_value_defs): param_values = [] for uno, due_values in param_value_defs: for due in due_values: param_values.append((uno, due)) return (param_names, param_values), {} @pytest.indirect_parametrize(custom_parametrization, ("uno", "due"), ( ("Fritula", (1, 2, 3, 4, 5, 6, 7, 8, 9)), ("Madagascar", (10, 30, 50)), ("Rumpelstiltskin", (20, 40)))) def test_example(uno, due): assert False """ import pytest def indirect_parametrize(func, *args, **kwargs): """ Decorator registering a custom parametrization function for a pytest test. This pytest.mark.indirect_parametrize() replacement allows the use of indirect parametrization functions taking no input parameters or, with pytest versions prior to 2.5.2, functions taking only keyword arguments. If a pytest.mark.indirect_parametrize() call is made with such an indirect parametrization function, it decorates the given function instead of storing and using it to decorate the intended function later on. """ # In pytest versions prior to 2.5.2 pytest.mark.indirect_parametrize() # special handling occurs even when passing it additional keyword arguments # so we have to make sure we are passing it at least one additional # positional argument. def wrapper(func, *args, **kwargs): return func(*args, **kwargs) return pytest.mark.indirect_parametrize(wrapper, func, *args, **kwargs) def pytest_configure(config): """Describe the new pytest marker in the --markers output.""" config.addinivalue_line("markers", "indirect_parametrize(function, argnames, argvalues): similar to the " "builtin pytest.mark.parametrize implementation, except that it takes " "an indirect parametrization function as an additional initial " "positional argument. All the other parameters are forwarded to the " "indirect parametrization function which then returns the actual " "metafunc.parametrize() parameters (standard Python positional " "argument list and keyword argument dictionary) based on the received " "input data. For more detailed information see the " "indirect_parametrize pytest plugin implementation module.") """pytest hook publishing references in the toplevel pytest namespace.""" pytest.indirect_parametrize = indirect_parametrize def pytest_generate_tests(metafunc): """pytest hook called for all detected test functions.""" mark = metafunc.definition.get_closest_marker('indirect_parametrize') if not mark: return args, kwargs = mark.args[0](*mark.args[1:], **mark.kwargs) metafunc.parametrize(*args, **kwargs)