Search code examples
pythonpytestpython-unittestnonetype

How to test with parametrize that something is None or not None?


I have a method that shall return None in some exceptional situations and otherwise something "complicated". In my dummy MWE below (not my real method!) there are two situations (x==0 and x==10) where None is returned.

    def foo(x: int) -> typing.Optional[float]:
    if x == 0 or x == 10:
        return None
    else:
        return 1/x

I have a unittest to test the "complicated" outcome in detail for "normal" situations. Now, I want to write a test specifically about whether the method returns None for the exceptional situations and something which is not None for the normal situation

@pytest.mark.parametrize("x, target",
                     [(0, None), (10, None), (5, not None)])
def test_foo(x, target):
    assert foo(x) is target

I hoped to have a test case assert foo(5) is not None, but the variable target which was not None is evaluated as True and thus assert foo(x) is True will fail. So how to test with parametrize that something is None or not None?


Solution

  • Put the desired condition in a function and call that on the result?

    @pytest.mark.parametrize("x, validator",[0, lambda x: x is None])
    def test_foo(x, validator):
        assert validator(foo(x))
    

    Of course, you can have two real functions is_none and not_none. Signals intent clearly enough.

    Alternatively, put some custom logic in your test fn, and use an Enum with two entries NONE and NOT_NONE to signal intent.

    Or most simply, just write the test explicitly:

    @pytest.mark.parametrize("x, is_none", [0, False])
    def test_foo_returns_none(x, is_none):
        res = foo(x)
        if is_none:
            assert res is None
        else:
            assert res is not None
    
    

    With all that said, you really should look at hypothesis as the answer says. It can be a fiddle to set up for some functions, but it's a really great tool. It can also be used to find pathological cases, which is otherwise pretty hard.