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

Michael Tom-Wing, Christie Wilson - Introductio...

Michael Tom-Wing, Christie Wilson - Introduction to Unit Testing in Python with Pytest

In this tutorial we’ll be taking you on a journey into the wonderful land of unit testing with pytest. We’ll be taking a step by step approach by iteratively adding unit test coverage to our awesome Cat In A Box™ project. You will also get a crash course on Git, Github, virtualenvs, and test automation. By the end, we hope that you’ll have a desire to bring testing to our own projects!

https://us.pycon.org/2016/schedule/presentation/1815/

PyCon 2016

May 29, 2016
Tweet

More Decks by PyCon 2016

Other Decks in Programming

Transcript

  1. pycon 2016 @mtomwing @bobcatwilson Prerequisites 1. A Github account with

    an SSH key https://help.github.com/articles/generating-ssh-keys/ 2. If you want to develop on your machine: 3. pip and virtualenv 4. git 5. (Optional) Fork and clone our repo! https://github.com/keeppythonweird/catinabox
  2. pycon 2016 @mtomwing @bobcatwilson Intro to Unit Testing in Python

    with PyTest Michael Tom-Wing @mtomwing Christie Wilson @bobcatwilson
  3. keeppythonweird @mtomwing @bobcatwilson • Software Engineers @ Demonware • Video

    Game Industry • Owned by Activision • Online services for games NOW ON TO TESTING! Obligatory Plug
  4. pycon 2016 @mtomwing @bobcatwilson Welcome to our tutorial!!!! Let’s find

    out a bit about why we’re all here What’s your role? http://www.strawpoll.me/10337223 Have you written a test before? http://www.strawpoll.me/10337208 How much Python experience do you have? http://www.strawpoll.me/10337237
  5. pycon 2016 @mtomwing @bobcatwilson Schedule • What is a test?

    • Initial environment setup • What are unit tests? • Write some tests. • What is test automation? • Run your tests through our automation. • What are some advanced testing techniques? • Write some tests using those techniques. • Q & A
  6. pycon 2016 @mtomwing @bobcatwilson Learning Outcomes • What tests are

    and why they are important • What unit tests are and why you should write them • How to approach writing unit tests • Why you need test automation and some options • Some ways to measure code / test quality • Mocking, fixtures, and parametrization - oh my! • Refactoring for unit testability • Hopefully none of our bad habits :)
  7. pycon 2016 @mtomwing @bobcatwilson What is a test? • Specifies

    how your software is intended to work • Can be run against your software to verify it
  8. pycon 2016 @mtomwing @bobcatwilson Why test? • Increase: ◦ Trust

    ◦ Confidence • You will never be 100% confident! • But you can be 60% confident.
  9. pycon 2016 @mtomwing @bobcatwilson Types of tests • Specifying and

    running tests for everything is: ◦ Hard to maintain ◦ Slow ◦ Hard to write
  10. pycon 2016 @mtomwing @bobcatwilson Types of tests • Unit tests

    ◦ Test ‘isolated units’ ▪ e.g. a method or function ◦ Super high coverage ◦ Most of the tests • Integration tests ◦ Combine units and test them together ◦ Fill in the cracks between the tests • System tests ◦ Test with everything plugged together and configured as expected ◦ From the end user’s perspective • Acceptance tests ◦ Test the customer’s use cases
  11. pycon 2016 @mtomwing @bobcatwilson Tutorial: Setup and run existing tests

    • https://github.com/keeppythonweird/catinabox/ • Follow along with /steps/1-run_tests.md ◦ Setup a virtualenv ◦ Run the existing tests
  12. pycon 2016 @mtomwing @bobcatwilson Optional - Use PythonAnywhere • Sign

    up for an account (the beginner tier is free!) • Start a bash session • Create an SSH key and upload it to Github ◦ $ ssh-keygen ◦ < hit enter a bunch of times > ◦ $ cat .ssh/id_rsa.pub ◦ < copy the output to Github> • Continue with the originally instructions at the “clone our repo” step
  13. pycon 2016 @mtomwing @bobcatwilson Coverage Statement coverage == “Was this

    line executed?” Decision coverage == “Was this code path executed?” Condition coverage == “Was every part of the decision executed?” statement decision condition
  14. pycon 2016 @mtomwing @bobcatwilson Unit Tests • People often love

    or hate unit tests. • But they are neutral, like brushing your teeth
  15. pycon 2016 @mtomwing @bobcatwilson What are unit tests good for?

    • Finding bugs DURING development • A design tool • Writing maintainable code • Documenting a developer’s intentions • Running quickly
  16. pycon 2016 @mtomwing @bobcatwilson What are unit tests not good

    for? • Finding bugs • Indicating that your application is functioning correctly • Testing glue code • Testing every possible permutation Tests Pass!
  17. pycon 2016 @mtomwing @bobcatwilson Generating test cases • Think about

    possible input • Categorize the input into special cases • One test per special case
  18. pycon 2016 @mtomwing @bobcatwilson How would we test this? •

    Input which IS a palindrome • Input which is NOT a palindrome
  19. pycon 2016 @mtomwing @bobcatwilson Trusting sources of input • What

    if the wrong type of data is passed in? • What if the sequence is extremely large? • Depends: ◦ Where the input is coming from ◦ Where you implement validation
  20. pycon 2016 @mtomwing @bobcatwilson How would we test this? -

    #2 is_valid_number(3) == True is_valid_number(1) == False is_valid_number(8) == False is_valid_number(6) == False is_leap_year(1757) == False is_leap_year(2004) == True is_leap_year(1900) == False is_leap_year(2000) == True
  21. pycon 2016 @mtomwing @bobcatwilson Python Unit Testing unittest module •

    Comes with the standard library • Typically will do basically everything you need • self.assertEqual(result, “cats”) pytest module • $ pip install pytest • Provides everything that unittest does but with more batteries included! • Less boilerplate thanks to magical fixtures. • Assertions are more natural and do not require custom invocation. • assert result == “cats” We’ll be using pytest in this tutorial.
  22. pycon 2016 @mtomwing @bobcatwilson pytest - how to 1. $

    pip install pytest 2. Create a module to hold your test (e.g. test_cool_stuff.py). 3. Write the test. 4. Run the test.
  23. pycon 2016 @mtomwing @bobcatwilson pytest - how to continued pytest

    will treat any function whose name starts with test_ a test. Same goes for test modules. We can use plain old Python assert to test that things are as we expected them to be.
  24. pycon 2016 @mtomwing @bobcatwilson Unit Test Structure 1. Define your

    inputs and any preconditions. 2. Invoke the thing. 3. Verify that it did what you expected. TL;DR a test is an easy way for you to quantify what it means for your thing to “work”.
  25. pycon 2016 @mtomwing @bobcatwilson PEP8 • It’s a coding standard

    • Prescribes things like: ◦ < 80 character lines ◦ 2 new lines between functions in a module ◦ 1 new line between methods in a class ◦ Visual indentation rules ◦ … and more! • PEP8 isn’t the only standard out there! (see Google’s Python Style Guide) • Main thing is to be consistent with the codebase • Our tests will fail if py.test finds any PEP8 violations :) • https://www.python.org/dev/peps/pep-0008/
  26. pycon 2016 @mtomwing @bobcatwilson Tutorial: Write your first test •

    https://github.com/keeppythonweird/catinabox • Follow along with steps/2-simple_function.md ◦ Finish writing the tests in test_catmath.py
  27. pycon 2016 @mtomwing @bobcatwilson Test Automation - System tests •

    Reduce developer burden ◦ Slower ◦ More difficult to set up
  28. pycon 2016 @mtomwing @bobcatwilson Travis CI • CI = Continuous

    Integration • Third party service that will “build” your Github projects ◦ “build” = “run the tests” in our case • Free for open source projects • We won’t be covering setting up Travis, but rest assured it is very simple! • Other CI services are available (e.g. Atlassian’s Bamboo) • https://travis-ci.org/keeppythonweird/catinabox
  29. pycon 2016 @mtomwing @bobcatwilson Coveralls • Third-party service for measuring

    statement coverage of your Github project • Free for open source projects • Track changes in coverage over time • https://coveralls.io/github/keeppythonweird/catinabox
  30. pycon 2016 @mtomwing @bobcatwilson Other Testable Aspects • Sometimes it’s

    also worth adding other checks to your testing pipeline. • Static Analysis: Done entirely offline - without running your code • Cyclomatic Complexity ◦ A measure of how complex a function is ◦ Checks that functions “aren’t too complex” ◦ $ pip install pytest-mccabe • PEP8 ◦ Checks for PEP8 compliance ◦ $ pip install pytest-pep8 • Pyflakes ◦ Checks for syntax errors ◦ $ pip install pytest-flakes • You can have these run before your tests in order to fail fast!
  31. pycon 2016 @mtomwing @bobcatwilson Tutorial: Create a pull request •

    https://github.com/keeppythonweird/catinabox • Follow along with steps/3-pull.md ◦ Commit your new tests ◦ Create a pull request from your fork BONUS! • If you finish early, review the other pull requests ◦ Be respectful and positive ◦ This presentation has great tips for effective code reviews: ▪ http://confreaks.tv/videos/railsconf2015-implementing-a-strong-code-review-culture
  32. pycon 2016 @mtomwing @bobcatwilson Trusting sources of input • What

    if we didn’t trust the input? • What other test cases might we have for cat_years_to_hooman_years?
  33. pycon 2016 @mtomwing @bobcatwilson Generating test cases • < 0

    • 0 • Fraction of a year • Most ages • > 1000 • Wrong data type • NaN
  34. pycon 2016 @mtomwing @bobcatwilson Advanced cat hooman • catinabox/safecatmath.py •

    Now checks that age_in_cat_years is an int or float. • Also makes sure the cat is not too young or too old.
  35. pycon 2016 @mtomwing @bobcatwilson pytest - fixtures Fixtures are a

    way to define reusable components that are required by your tests. Pytest will automagically hook up your fixtures to your tests (or other fixtures!) that require them. See https://pytest.org/latest/builtin.html for more information on the built-in fixtures provided by pytest.
  36. pycon 2016 @mtomwing @bobcatwilson pytest - fixtures continued By default,

    fixtures are recreated for every test that requires them. It is possible to control the lifetime of a fixture (e.g. create it once for all the tests), but that is out of scope for today! See https://pytest.org/latest/fixture.html.
  37. pycon 2016 @mtomwing @bobcatwilson Tutorial: Testing classes with fixtures •

    https://github.com/keeppythonweird/catinabox • Follow along with steps/5-classes.md
  38. pycon 2016 @mtomwing @bobcatwilson Unit testing and state of the

    outside world • What if you want to test functionality that: ◦ Uses the current time/sleeps ◦ Depends on an external service (e.g. an HTTP server or DB) ◦ Uses random • Super easy in Python!!!
  39. pycon 2016 @mtomwing @bobcatwilson Mocking • Create “mock” objects that

    mimic the external objects/functions • You can control their behaviour completely! ◦ Return whatever time you want ◦ Pretend to sleep ◦ Return fake DB or HTTP results ◦ Return deterministic results instead of random • Verify arguments used • Verify that everything is plugged together correctly ◦ Test the true behaviour later with system tests
  40. pycon 2016 @mtomwing @bobcatwilson Mocking • mock ◦ pip install

    mock • Included in the Python 3 standard lib
  41. pycon 2016 @mtomwing @bobcatwilson Patching with pytest • pytest-mock ◦

    pip install pytest-mock ◦ Wrapper around the mock library the works well with pytest
  42. pycon 2016 @mtomwing @bobcatwilson Mock and Patch - autospec •

    Make sure that the expected interface is being ◦ Raises if methods or attributes are used that don’t exist on the mocked object • Always use autospec!
  43. pycon 2016 @mtomwing @bobcatwilson Tutorial: Control time with mock •

    https://github.com/keeppythonweird/catinabox • Follow along with steps/6-mock.md
  44. pycon 2016 @mtomwing @bobcatwilson Parameterization - condensing tests • cat_years_to_hooman_years

    • What if we wanted to test for more bad input? ◦ So many more tests to write!
  45. pycon 2016 @mtomwing @bobcatwilson Parameterization of fixtures • Fixtures can

    be parametrized too! • py.test will automatically run every permutation of tests and fixtures
  46. pycon 2016 @mtomwing @bobcatwilson Unit testability and well factored code

    • Lots of code is hard to unit test • Usually not well factored • Refactoring for unit testability = higher quality code
  47. pycon 2016 @mtomwing @bobcatwilson catinabox/examples/test_complected.py • Hard to write •

    Hard to read • Hard to maintain • Adds little • Copy pasta Example: Test for poorly factored code
  48. pycon 2016 @mtomwing @bobcatwilson Well factored code • Highly cohesive

    • Loosely coupled • Does one thing • Isolate glue code (avoid complecting)* * Rich Hickey: https://www.infoq.com/presentations/Simple-Made-Easy
  49. pycon 2016 @mtomwing @bobcatwilson Tutorial: Refactoring for unit testability •

    https://github.com/keeppythonweird/catinabox • Follow along with steps/8-refactor.md
  50. pycon 2016 @mtomwing @bobcatwilson Refactor for testability: Group code review

    As a group review solution: refactored catgenerator and its tests • Is the code better or worse? ◦ Which parts are better? ◦ Which parts are worse? • Is the code well tested? • How readable is the test?