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

Justin Crown - "WHAT IS THIS MESS?" - Writing tests for pre-existing code bases

Justin Crown - "WHAT IS THIS MESS?" - Writing tests for pre-existing code bases

Many of us practice test driven development, and pride ourselves in our code coverage. This is relatively easy to do when you begin a new project, but what happens when you take over an existing code base with little to no tests? Where and how do you start writing tests? This task can be very intimidating and frustrating, but can be accomplished!

This talk will run through some common approaches and methodologies for adding test coverage to pre-existing code (that you might not even be familiar with at all). The next time you take over an untested monolith, you will be able to do the right thing and start writing tests instead of hoping for the best!

https://us.pycon.org/2018/schedule/presentation/88/

PyCon 2018

May 11, 2018
Tweet

More Decks by PyCon 2018

Other Decks in Programming

Transcript

  1. WHAT IS THIS MESS?
    (Writing tests for pre-existing
    code bases)

    View Slide

  2. Justin Crown
    https://github.com/mrname/
    WOW!

    View Slide

  3. View Slide

  4. What Is Legacy Code?
    • Lacking Tests
    • Lacking Documentation
    • Difficult to read or reason about

    View Slide

  5. Why Should We Write Tests
    For Legacy Code?

    View Slide

  6. Why SHOULDN’T We?
    It will be replaced eventually
    NO IT WON’T
    (maybe it will... but not right now)

    View Slide

  7. Why SHOULDN’T We?
    It has been working just fine
    HAS IT?
    If it has... do YOU want to break it?

    View Slide

  8. Why SHOULDN’T We?
    I do not have time
    It takes more time (and money) to
    debug issues in the wild
    It will take less time (and money) to work
    with in the future

    View Slide

  9. Why SHOULDN’T We?
    I’m Lazy
    LAZY means being too lazy to fight fires,
    not being too lazy to write a test

    View Slide

  10. Why Do I Have To Be The
    One To Start?

    View Slide

  11. View Slide

  12. View Slide

  13. Test What You Touch
    • Write a test that asserts current behavior...
    BEFORE YOU TOUCH CODE
    • Write a test that asserts new/modified behavior.
    LET IF FAIL
    • Do code stuffs
    • Tests pass
    • Profit Significantly

    View Slide

  14. View Slide

  15. Choose Thy Weapons
    • Find a test runner that works for you
    • Use pre-existing tools for your framework/modules
    • Create reusable test scaffolding

    View Slide

  16. Choose Thy Weapons
    • pytest for test runner
    • pytest-django - utilities for django integration
    • pytest-socket - disable socket JUST IN CASE
    • pytest-cov - code coverage reports
    • factory_boy - avoid boilerplate when creating
    model instances

    View Slide

  17. Choose Thy Weapons
    Requirements File

    View Slide

  18. Choose Thy Weapons
    Test Runner Configuration

    View Slide

  19. Assignment 1
    Feature Request
    (alter existing functionality)
    Update “Quark” model to conditionally determine the value of the
    “magic” property

    View Slide

  20. Assignment 1
    New VIP Client!
    Master Of
    Quarks

    View Slide

  21. Assignment 1

    View Slide

  22. Assignment 1

    View Slide

  23. Assignment 1
    Scaffolding - tests/model_factory.py

    View Slide

  24. Assignment 1
    Unit Test
    • Should cover the smallest piece of code possible
    • Should run fast
    • Should not use a database, file I/O, etc.

    View Slide

  25. Assignment 1
    Unit Test (Test Class)

    View Slide

  26. Assignment 1
    Unit Test (Test Existing Functionality)

    View Slide

  27. Assignment 1
    Unit Test (Test Existing Functionality)

    View Slide

  28. Assignment 1
    Unit Test (Test NEW Functionality)

    View Slide

  29. Assignment 1
    Unit Test (Test NEW Functionality)

    View Slide

  30. Assignment 1
    Unit Test (Test NEW Functionality)

    View Slide

  31. Assignment 1
    Unit Test (Test NEW Functionality)

    View Slide

  32. View Slide

  33. View Slide

  34. Assignment 1
    There is a Problem Still......
    WAT

    View Slide

  35. Assignment 1
    Where Did We Go Wrong?
    We should test the FUNCTIONALITY of legacy applications
    (integration testing)

    View Slide

  36. Assignment 1

    View Slide

  37. Assignment 1
    Integration Test

    View Slide

  38. Assignment 1
    WHYYYYYYYYYY

    View Slide

  39. Assignment 2

    View Slide

  40. Assignment 2
    Dealing With Code That Touches The World
    Fixing a bug in a method that has side effects

    View Slide

  41. Assignment 2
    Give It An Array Of Integers

    View Slide

  42. Assignment 2
    Get Back a New Array With Crazy Sauce On Top

    View Slide

  43. Assignment 2
    Let’s Read the Code But Don’t Touch

    View Slide

  44. Assignment 2
    Let’s Read the Code But Don’t Touch

    View Slide

  45. Assignment 2
    Where to Start?
    Let’s just try to get the class in a test harness

    View Slide

  46. Assignment 2

    View Slide

  47. Breaking
    Dependencies

    View Slide

  48. Assignment 2
    Subclassing

    View Slide

  49. Assignment 2
    Subclassing

    View Slide

  50. Assignment 2
    Let’s Test Out The View

    View Slide

  51. Assignment 2
    Let’s Test Out The View

    View Slide

  52. Assignment 2
    Mocking

    View Slide

  53. Assignment 2
    Mocking

    View Slide

  54. Assignment 2
    Make The World A Better Place

    View Slide

  55. Assignment 2
    Fix Things

    View Slide

  56. Assignment 2
    Hold Up Now

    View Slide

  57. Assignment 2
    We could have broken things using that mutated argument

    View Slide

  58. Assignment 2

    View Slide

  59. Assignment 2
    Backs Against The Wall

    View Slide

  60. Assignment 2
    Sometimes it makes sense to change code to make it more testable
    But ONLY if we can do that without breaking the interface
    And preferably only if it makes ACTUAL sense

    View Slide

  61. Assignment 2
    Sometimes it makes sense to change code to make it more testable
    But ONLY if we can do that without breaking the interface
    And preferably only if it makes ACTUAL sense

    View Slide

  62. Assignment 3
    Adding New Features To Existing (and Unruly) Things

    View Slide

  63. Assignment 3
    Adding New Features To Existing (and Unruly) Things

    View Slide

  64. Assignment 3
    Assignment - Add extra reporting (SMS)

    View Slide

  65. Assignment 3
    I want the full thing under test before I start

    View Slide

  66. Assignment 3
    Mock Our Way Through It?

    View Slide

  67. Assignment 3
    Mock Our Way Through It!
    Advantages
    • Existing code is fully tested without changing anything
    Disadvantages
    • Difficult to implement (tons of mocks for a single test)
    • Tests are brittle (tied to the implementation itself)

    View Slide

  68. Assignment 3
    Refactor?

    View Slide

  69. Assignment 3
    Refactor?

    View Slide

  70. Assignment 3
    Refactor?
    Advantages
    • Code is easier to read and work with
    • Code is easier to test
    Disadvantages
    • Might break something
    • Not directly required to implement feature

    View Slide

  71. Assignment 3
    I have no time but need to add the thing

    View Slide

  72. Assignment 3
    Just Write The Code In The Existing Method
    Advantages
    • Quickest way to get the job done
    Disadvantages
    • Code is untested
    • Highly likely to break something
    • You just made the code even worse

    View Slide

  73. Assignment 3
    Make a New Method. Call It From The Old One

    View Slide

  74. Assignment 3
    Make a New Method. Call It From The Old One
    Advantages
    • We can write a small piece of code that we can test
    Disadvantages
    • We cannot test the way the calling method uses our code

    View Slide

  75. Assignment 3
    Subclass

    View Slide

  76. Assignment 3
    Subclass
    Advantages
    • We can add functionality without affecting existing class
    • Tests are better, because we can mock super() to test the way
    they work together
    Disadvantages
    • Increased complexity of code base
    • Must replace existing class instances with new one

    View Slide

  77. Are We Making Progress?
    Using Code Coverage Reports

    View Slide

  78. Are We Making Progress
    Using Code Coverage Reports

    View Slide

  79. Are We Making Progress
    Using Code Coverage Reports

    View Slide

  80. Are We Making Progress
    Using Code Coverage Reports

    View Slide

  81. Create A Legacy
    (the good kind)

    View Slide

  82. Create A Legacy
    Pushing code should run a build that runs tests
    Builds should fail if tests fail
    CI/CD
    Builds should fail if test coverage drops

    View Slide

  83. Create A Legacy
    Start testing things you don’t need to test
    Test Sprints
    Keep Testing!
    Brain refreshment

    View Slide

  84. View Slide