| ``nose-parameterized`` is a decorator for parameterized testing with ``nose`` |
| ============================================================================= |
| |
| *Now with 100% less Python 3 incompatibility!* |
| |
| Nose. It's got test generators. But they kind of suck: |
| |
| * They often require a second function |
| * They make it difficult to separate the data from the test |
| * They don't work with subclases of ``unittest.TestCase`` |
| * ``kwargs``? What ``kwargs``? |
| |
| But ``nose-parameterized`` fixes that: |
| |
| .. code:: python |
| |
| # test_math.py |
| from nose.tools import assert_equal |
| from nose_parameterized import parameterized |
| |
| import unittest |
| import math |
| |
| @parameterized([ |
| (2, 2, 4), |
| (2, 3, 8), |
| (1, 9, 1), |
| (0, 9, 0), |
| ]) |
| def test_pow(base, exponent, expected): |
| assert_equal(math.pow(base, exponent), expected) |
| |
| class TestMathUnitTest(unittest.TestCase): |
| @parameterized.expand([ |
| ("negative", -1.5, -2.0), |
| ("integer", 1, 1.0), |
| ("large fraction", 1.6, 1), |
| ]) |
| def test_floor(self, name, input, expected): |
| assert_equal(math.floor(input), expected) |
| |
| :: |
| |
| $ nosetests -v test_math.py |
| test_math.test_pow(2, 2, 4) ... ok |
| test_math.test_pow(2, 3, 8) ... ok |
| test_math.test_pow(1, 9, 1) ... ok |
| test_math.test_pow(0, 9, 0) ... ok |
| test_floor_0_negative (test_math.TestMathUnitTest) ... ok |
| test_floor_1_integer (test_math.TestMathUnitTest) ... ok |
| test_floor_2_large_fraction (test_math.TestMathUnitTest) ... ok |
| |
| ---------------------------------------------------------------------- |
| Ran 7 tests in 0.002s |
| |
| OK |
| |
| |
| Exhaustive Usage Examples |
| -------------------------- |
| |
| The ``@parameterized`` and ``@parameterized.expand`` decorators accept a list |
| or iterable of tuples or ``param(...)``, or a callable which returns a list or |
| iterable: |
| |
| .. code:: python |
| |
| from nose_parameterized import parameterized, param |
| |
| # A list of tuples |
| @parameterized([ |
| (2, 3, 5), |
| (3, 5, 8), |
| ]) |
| def test_add(a, b, expected): |
| assert_equal(a + b, expected) |
| |
| # A list of params |
| @parameterized([ |
| param("10", 10), |
| param("10", 16, base=16), |
| ]) |
| def test_int(str_val, expected, base=10): |
| assert_equal(int(str_val, base=base), expected) |
| |
| # An iterable of params |
| @parameterized( |
| param.explicit(*json.loads(line)) |
| for line in open("testcases.jsons") |
| ) |
| def test_from_json_file(...): |
| ... |
| |
| # A callable which returns a list of tuples |
| def load_test_cases(): |
| return [ |
| ("test1", ), |
| ("test2", ), |
| ] |
| @parameterized(load_test_cases) |
| def test_from_function(name): |
| ... |
| |
| .. ** |
| |
| Note that, when using an iterator or a generator, Nose will read every item |
| into memory before running any tests (as it first finds and loads every test in |
| each test file, then executes all of them at once). |
| |
| The ``@parameterized`` decorator can be used test class methods, and standalone |
| functions: |
| |
| .. code:: python |
| |
| from nose_parameterized import parameterized |
| |
| class AddTest(object): |
| @parameterized([ |
| (2, 3, 5), |
| ]) |
| def test_add(self, a, b, expected): |
| assert_equal(a + b, expected) |
| |
| @parameterized([ |
| (2, 3, 5), |
| ]) |
| def test_add(a, b, expected): |
| assert_equal(a + b, expected) |
| |
| |
| And ``@parameterized.expand`` can be used to generate test methods in |
| situations where test generators cannot be used (for example, when the test |
| class is a subclass of ``unittest.TestCase``): |
| |
| .. code:: python |
| |
| import unittest |
| from nose_parameterized import parameterized |
| |
| class AddTestCase(unittest.TestCase): |
| @parameterized.expand([ |
| ("2 and 3", 2, 3, 5), |
| ("3 and 5", 2, 3, 5), |
| ]) |
| def test_add(self, _, a, b, expected): |
| assert_equal(a + b, expected) |
| |
| Will create the test cases:: |
| |
| $ nosetests example.py |
| test_add_0_2_and_3 (example.AddTestCase) ... ok |
| test_add_1_3_and_5 (example.AddTestCase) ... ok |
| |
| ---------------------------------------------------------------------- |
| Ran 2 tests in 0.001s |
| |
| OK |
| |
| Note that ``@parameterized.expand`` works by creating new methods on the test |
| class. If the first parameter is a string, that string will be added to the end |
| of the method name. For example, the test case above will generate the methods |
| ``test_add_0_2_and_3`` and ``test_add_1_3_and_5``. |
| |
| The names of the test cases generated by ``@parameterized.expand`` can be |
| customized using the ``testcase_func_name`` keyword argument. The value should |
| be a function which accepts three arguments: ``testcase_func``, ``param_num``, |
| and ``params``, and it should return the name of the test case. |
| ``testcase_func`` will be the function to be tested, ``param_num`` will be the |
| index of the test case parameters in the list of parameters, and ``param`` |
| (an instance of ``param``) will be the parameters which will be used. |
| |
| .. code:: python |
| |
| import unittest |
| from nose_parameterized import parameterized |
| |
| def custom_name_func(testcase_func, param_num, param): |
| return "%s_%s" %( |
| testcase_func.__name__, |
| parameterized.to_safe_name("_".join(str(x) for x in param.args)), |
| ) |
| |
| class AddTestCase(unittest.TestCase): |
| @parameterized.expand([ |
| (2, 3, 5), |
| (2, 3, 5), |
| ], testcase_func_name=custom_name_func) |
| def test_add(self, a, b, expected): |
| assert_equal(a + b, expected) |
| |
| Will create the test cases:: |
| |
| $ nosetests example.py |
| test_add_1_2_3 (example.AddTestCase) ... ok |
| test_add_2_3_5 (example.AddTestCase) ... ok |
| |
| ---------------------------------------------------------------------- |
| Ran 2 tests in 0.001s |
| |
| OK |
| |
| |
| The ``param(...)`` helper class stores the parameters for one specific test |
| case. It can be used to pass keyword arguments to test cases: |
| |
| .. code:: python |
| |
| from nose_parameterized import parameterized, param |
| |
| @parameterized([ |
| param("10", 10), |
| param("10", 16, base=16), |
| ]) |
| def test_int(str_val, expected, base=10): |
| assert_equal(int(str_val, base=base), expected) |