Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Unit Testing in Python using pytest - BelPy 2021

Unit Testing in Python using pytest - BelPy 2021

Rohit Sanjay

January 30, 2021
Tweet

More Decks by Rohit Sanjay

Other Decks in Technology

Transcript

  1. Hi I’m Rohit Sanjay. - Final Year Engineering Undergraduate at

    MIT Manipal - Author and maintainer of testbook (under nteract org) - Incoming Software Engineer Intern at Goldman Sachs, Bengaluru rohitsanjay.com
  2. --Boris Beizer, in "Software testing techniques" [source] More than the

    act of testing, the act of designing tests is one of the best bug preventers known.
  3. Agenda • Introduction to unit testing • Writing unit tests

    ❖ unittest vs pytest ❖ Mocking ❖ Using context managers ❖ PyTest features - f i xtures and parametrized tests
  4. • A unit test is a test that checks that

    a single component operates in the right way. • A unit test helps you to isolate what is broken in your application and fix it faster. Unit Testing in two sentences That’s it! Moving on..
  5. • Too much boilerplate code is needed. • The API

    can make the test code's intent hard to understand • camelCase naming unittest
  6. • Detailed info on failing assert statements • Auto-discovery of

    test modules and functions (searches for test_*.py or *_test.py files) • Modular fixtures for managing small or parametrized long-lived test resources • Tests parametrization • Can run unittest test suites out of the box • Rich plugin architecture, with over 315+ external plugins and thriving community Why pytest is the better option..
  7. A little about side_effect When side_effect is an iterable Let

    us get back to when we should use mocks
  8. • System calls • Network/API calls • I/O operations •

    Unpredictable results (random) When to use mocks
  9. • It’s a simple “protocol” (or interface) that your object

    needs to follow so it can be used with the with statement. • Basically all you need to do is add __enter__ and __exit__ methods to an object if you want it to function as a context manager. • Python will call these two methods at the appropriate times in the resource management cycle. Context Managers in Python Are you with me?
  10. Context managers in unit tests jovian/tests/resources/shared.py @contextmanager def temp_directory(): orig_path

    = os.getcwd() try: with TemporaryDirectory() as dir: os.chdir(dir) yield dir finally: os.chdir(orig_path)
  11. @contextmanager def mock_git_repo(): orig_dir = os.getcwd() with temp_directory() as dir:

    os.mkdir("mock_git_repo") os.chdir("mock_git_repo") commands = textwrap.dedent(""" git init git remote add origin https://github.com/JovianML/mock_repo.git touch notebook.ipynb git add . git commit -m "initialcommit" """).splitlines()[1:] for command in commands: check_call(command.split()) yield dir os.chdir(orig_dir) jovian/tests/resources/shared.py
  12. • capsys - Capture, as text, output to sys.stdout and

    sys.stderr. • testdir - Provide a temporary test directory to aid in running, and testing, pytest plugins. • tmp_path - Provide a pathlib.Path object to a temporary directory which is unique to each test function. • tmpdir - Provide a py.path.local object to a temporary directory which is unique to each test function; replaced by tmp_path. pytest fixtures
  13. Custom pytest fixtures import pytest @pytest.fixture def smtp(): import smtplib

    return smtplib.SMTP("smtp.gmail.com") def test_ehlo(smtp): response, msg = smtp.ehlo() assert response == 250 assert 0 # for demo purposes
  14. Custom pytest fixtures import pytest from click.testing import CliRunner @pytest.fixture(scope='module')

    def runner(): # Get instance of CliRunner runner = CliRunner() return runner jovian/tests/utils/test_cli.py
  15. pytest parametrize import pytest @pytest.mark.parametrize( "test_input, expected", [ ("3+5", 8),

    ("2+4", 6), ("6*9", 42) ] ) def test_eval(test_input, expected): assert eval(test_input) == expected
  16. pytest parametrize @pytest.mark.parametrize( "urls, expected_result", [ (["https://jovian.ml"], "https://jovian.ml"), (["https://jovian.ml/"], "https://jovian.ml/"),

    (["///user/siddhant"], "user/siddhant"), (["https://jovian.ml", "user/siddhant"], "https://jovian.ml/user/siddhant"), (["https://jovian.ml", "/user/siddhant"], "https://jovian.ml/user/siddhant"), ] ) def test_urljoin(urls, expected_result): assert urljoin(*urls) == expected_result jovian/tests/utils/test_misc.py
  17. • To mock, or not to mock - PyCon 2016

    • Context Managers and the “with” Statement in Python References