pytest-mypy icon indicating copy to clipboard operation
pytest-mypy copied to clipboard

Expected mypy errors

Open dcbaker opened this issue 5 years ago • 4 comments

I'm working on a module specifically for use with typing, so I have tests that I expect to raise mypy warnings. It doesn't look like there's a way to mark tests as having expected errors.

dcbaker avatar Oct 03 '20 22:10 dcbaker

I'm not sure I understand the issue :thinking: Could you elaborate or provide an example? My initial reaction is that controlling/suppressing mypy errors seems like a mypy configuration issue.

dmtucker avatar Oct 06 '20 03:10 dmtucker

What I want is basically the equivalent of:

def test_func():
    with pytest.raises(Exception):
       func('Invalid input')

but for mypy errors. I want to verify that an error is happening. My use case is that I'm writing "const" protocols (think list with out any methods or operators that modify), and I want to ensure that if I have:

class MyProto(Protocol[T]):

    def __add__(self, other: T) -> MyProto[T]: ...

class MyClass(Generic[T])
    def __add__(self, other: T) -> MyClass[T]:
    def __iadd__(self, other: T) -> None:


def test_func() -> None:
    c: MyProto[str] = MyClass(['foo'])
    with mypyerror:
        c += 'bar'  # this is an error, getting it is a test pass, not getting it is a test fail

dcbaker avatar Oct 07 '20 16:10 dcbaker

Interesting... I'm not aware of a way to do that, as written, but perhaps doing the same kind of thing the pytest-mypy tests do would work. For example,

def test_func(testdir):
    testdir.makepyfile('''
        class MyProto(Protocol[T]):
            def __add__(self, other: T) -> MyProto[T]: ...

        class MyClass(Generic[T]):
            def __add__(self, other: T) -> MyClass[T]: ...
            def __iadd__(self, other: T) -> None: ...

        c: MyProto[str] = MyClass(['foo'])
        c += 'bar'
    ''') 
    result = testdir.runpytest('--mypy') 
    result.assert_outcomes(failed=2)  # mypy exit status check fails too
    result.stdout.fnmatch_lines([ 
        '9: error: bad type error message etc*', 
    ])

In fact, you don't even really need pytest to be involved:

def test_func(testdir):
    testdir.makepyfile('''
        c: MyProto[str] = MyClass(['foo'])
        c += 'bar'
    ''') 
    result = testdir.run('mypy', 'test_func.py') 
    result.stdout.fnmatch_lines([ 
        'test_func.py:9: error: bad type error message etc*', 
    ])

dmtucker avatar Oct 11 '20 16:10 dmtucker

Can't you just add a type: ignore[error-code] on that line ? Make sure that the following codes are enabled:

  • https://mypy.readthedocs.io/en/stable/error_code_list2.html#check-that-type-ignore-comment-is-used-unused-ignore
  • https://mypy.readthedocs.io/en/stable/error_code_list2.html#check-that-type-ignore-include-an-error-code-ignore-without-code

Then if the suppression comment is unused or incorrect, test will fail with a mypy error.

That's how typeshed does its static-type-checking tests.

Avasam avatar Aug 29 '24 16:08 Avasam