Search code examples
pythonpython-3.xpytest

Test one function, with multiple test cases but only use one assert?


I have a class method that takes a list and determines if the list is valid or not according to a function.

I want to test it on three lists that are stored as static variables because they are used in other unit tests later on in the code.

def test__validate(self):
    decoder = Validator()
    slow_valid = Validator.validate(TestValidator.list_slow)
    med_valid = Validator.validate(TestValidator.list_med)
    fast_valid = Validator.validate(TestValidator.list_fast)


    assert slow_valid == True
    assert med_valid == False
    assert fast_valid == False

What is the correct way to remove the multiple assert statements?

Do I defined multiple versions of test__validate or are the multiple assert statements okay from a best practices position?


Solution

  • As proposed by @IanShelvington, the best practise for repeated tests with changed input (and result, in your case) is test parametrization. With pytest, you could do something like this:

    import pytest
    
    @pytest.mark.parametrize("val_list, result",
                             [(TestValidator.list_slow, True),
                              (TestValidator.list_med, False),
                              (TestValidator.list_fast, False)])
    
    def test_validate(val_list, result):
        assert Validator().validate(val_list) == result
    

    This gives you the output:

    ============================= test session starts =============================
    ...
    collecting ... collected 3 items
    
    param_result.py::test_validate[val_list0-True] PASSED                    [ 33%]
    param_result.py::test_validate[val_list1-False] PASSED                   [ 66%]
    param_result.py::test_validate[val_list2-False] PASSED                   [100%]
    
    ============================== 3 passed in 0.04s ==============================
    

    As you can see, this creates 3 separate tests, with the parameters in the name, so a failing test can easily be identified.

    If you want customized names of the shown tests, you can provide them using ids:

    @pytest.mark.parametrize("val_list, result",
                             [(TestValidator.list_slow, True),
                              (TestValidator.list_med, False),
                              (TestValidator.list_fast, False)],
                             ids=('slow', 'med', 'fast'))
    ...
    

    This would output:

    ============================= test session starts =============================
    ...
    param_result.py::test_validate[slow] PASSED                              [ 33%]
    param_result.py::test_validate[med] PASSED                               [ 66%]
    param_result.py::test_validate[fast] PASSED                              [100%]
    
    ============================== 3 passed in 0.06s ==============================