Skip to content

Instantly share code, notes, and snippets.

@bbarker
Created November 7, 2017 16:13
Show Gist options
  • Save bbarker/4ddf4a1c58ae8465f3d37b6f2234a421 to your computer and use it in GitHub Desktop.
Save bbarker/4ddf4a1c58ae8465f3d37b6f2234a421 to your computer and use it in GitHub Desktop.
Using mypy in tests

The following test uses mypy to check the correctness of Python code using typing, largely introduced in 3.5 and 3.6. The two tests check all package code and all test code in the tests directory, respectively. Testing frameworks such as 'nose2' will automatically run the tests and will fail if any typing error is detected (see step #3 below for more on this).

Steps to use

  1. Add the file below to your tests directory, which should be at the top-level in your repository alongside your module directory.
  2. Modify the first line in __init__ that sets self.pkgname to match your library's package name. This assumes one package per repo, so the test would need to be changed if this isn't how the repo structure works.
  3. If you run across typechecking errors in your code and you have a good reason for mypy to ignore them, you should be able to add # type: ignore along with an actual comment describing why the type checking should be ignored on this line.
import os
import glob
import subprocess
import unittest
from typing import List


class MyPyTest(unittest.TestCase):

    def test_run_mypy_module(self):
        """Run mypy on all module sources"""
        mypy_call: List[str] = ["mypy"] + self.mypy_opts + ["-p", self.pkgname]
        browse_result: int = subprocess.call(mypy_call, env=os.environ, cwd=self.pypath)
        self.assertEqual(browse_result, 0, 'mypy on browse')

    def test_run_mypy_tests(self):
        """Run mypy on all tests in module under the tests directory"""
        for test_file in glob.iglob(f'{os.getcwd()}/tests/**/*.py', recursive=True):
            mypy_call: List[str] = ["mypy"] + self.mypy_opts + [test_file]
            test_result: int = subprocess.call(mypy_call, env=os.environ, cwd=self.pypath)
            self.assertEqual(test_result, 0, f'mypy on test {test_file}')

    def __init__(self, *args, **kwargs) -> None:
        self.pkgname: str = "browse"
        super(MyPyTest, self).__init__(*args, **kwargs)
        my_env = os.environ.copy()
        self.pypath: str = my_env.get("PYTHONPATH", os.getcwd())
        self.mypy_opts: List[str] = ['--ignore-missing-imports']


if __name__ == '__main__':
    unittest.main()
@WhiteHotLoveTiger
Copy link

Thanks for this. What's the license if I'd like to include it in my project?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment