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

Learn To Test Like A Grumpy Programmer

Learn To Test Like A Grumpy Programmer

Slides from the testing workshop I did at PHP Australia Conference on April 14, 2016


Chris Hartjes

April 14, 2016


  1. Learn To Test Like A Grumpy Programmer PHP Australia Conference

    April 14, 2016
  2. Who is this guy and why should I listen? •

    Long-time tester • Beard conveys authority • PHP dev since 1998 • Wants to help you get better!
  3. 5 W’s (and one H)

  4. Who Should Test? • developers looking for stable deployments •

    developers looking to increase ability to change • developers looking to go home on time

  6. Stable Deployment • Exposes poor deployment practices • Exposes brittle

    application architectures • Promotes “automation-first” thinking
  7. Confidence In Changes • Did that new feature break an

    old one? • Did the refactoring break working code? • Did we miss an opportunity to move forward?
  8. Confidence In Changes • Removing technical debt is always scary

    • Increases reliance on automated systems • Forces thinking about more than just code
  9. Go Home On Time • Production pushes will go smoother

    • Eliminates “stick around just in case” deployments • Makes deployments a non-issue
  10. What Is TDD? • Design Tool • Style Enforcer •

    Hard-to-shake Coding Habit
  11. Design Tool • Tests as working examples of code •

    Forces you to concentrate on interfaces and API’s
  12. Design Tool • Limits work in progress • Exposes awkward

    code earlier • Exposes dependencies for code earlier
  13. Code In A Specific Style • Impossible to build fast-running

    unit tests for tightly-coupled code • Modules of code end up like LEGO bricks • “Ports and Adapters” • Dependencies become crystal clear
  14. You Can’t Go Back • “How do I test this

    thing?” • “How do I use this thing?” • “How can I change this thing without breaking it?”
  15. You Can’t Go Back • You stop using 3rd party

    tools without tests • You start re-evaluating how you build code for others • You pay much more attention to interfaces and API’s
  16. Where Do You Write Tests? • In development environments •

    As part of the NORMAL development process • When not prototyping
  17. Done By Developers • Cheapest time to find bugs is

    during development • TDD is an incredibly effective design process • Studies have proven it’s effectiveness
  18. Not When Prototyping • No need to write tests when

    you are experimenting • Once you’ve committed to the idea then TDD takes over to design interfaces and API’s
  19. Studies Prove It’s Value • 15% to 30% more development

    time • 40% to 90% fewer bugs (http://research.microsoft.com/en-us/groups/ese/nagappan_tdd.pdf)
  20. “For just one extra day a week you can have

    90% fewer bugs show up in production!”
  21. When Are Tests Written? • During initial development of functionality

    • Whenever a bug is reported and needs to be fixed • “Test-after” an anti-pattern but sometimes required
  22. During Initial Development • Tests force you to write code

    in a certain way • Lost opportunity costs are a real thing • Burnout from having to work overtime to fix bugs is a real thing
  23. Test-after Considered Harmful • Tests that are decoupled from the

    act of creating the code are often brittle • Brittle tests are hard to maintain, often randomly failing • Randomly failing tests get ignored
  24. Opportunity Costs X vs. 10X vs. 1000X

  25. Why Write Tests? • TDD encourages modular code with explicit

    dependencies • Refactoring and other sweeping changes impossible to do quickly without tests • Tests are stealth documentation on how to use your code
  26. Modular Code • TDD makes you focus on loosely-coupled code

    because of the focus on dependency management • Tightly-coupled code impossible to test without committing huge resources to “monkey-patching” tools or additional infrastructure • The intent of modular code is very clear
  27. Refactoring Much Easier • How would you check if your

    code works with a newer version of PHP? • How could you check if your code works after massive search-and-replace for PSR-2 fixes • How could you test replacing DB calls with calls to a JSON API?
  28. Tests As Stealth Documentation • “Write tests as if the

    code is already working” • They show which dependencies are necessary • They show how the code is actually used
  29. Building Blocks It’s Like Using Lego

  30. It’s Like Lego! • TDD encourages creating applications by combining

    units together like Legos • Results in loosely-coupled modules • Unit testing tools are no different
  31. Assertions • The backbone of unit testing • Simple to

  32. Assertions • Unit tests (usually) usually have one or more

    assertions • Proves that your expectation of an outcome is correct
  33. The Simplest Test That Runs

  34. Assertions • $this->assertTrue(<some expression>) • $this->assertFalse(<some expression>) • $this->assertEquals(<value 1>,

    <value 2)
  35. Assertions Those three assertions will cover 99% of your unit

    testing needs
  36. Test Doubles • Understanding them was the most difficult thing

    I had to learn • Makes you understand how critical putting dependencies in specific states is
  37. Test Doubles • Used as a substitute for the dependencies

    your code need • Set to a specific state depending on the scenario • Understanding Dependency Injection required
  38. Dependency Injection In Unit Tests • Figure out your dependencies

    • Figure out which ones need to be doubles • “Inject” them for your code-under-test to use
  39. Dependency Injection Techniques • Globally-available containers • Constructor injection •

    Setter injection
  40. Globally-available Containers • Best for legacy code where refactoring to

    injection is difficult • Can use $GLOBALS super global in a pinch • Container / service locator usage very common
  41. Constructor Injection • Pass in dependencies at object creation •

    Gets messy if many dependencies are required • Can lead to __construct() doing too much work
  42. Setter Injection • “Less messy” than using constructors • Refactoring

    to add get/set methods not overly intrusive • Allows overriding of internally-created dependencies
  43. Types Of Test Doubles • Classical definition is that there

    are five types • Dummy objects, test stubs, test spies, test mocks, test fakes
  44. Types Of Test Doubles • PHPUnit-compatible test double tools tend

    to only use three • Dummy objects, test stubs, test mocks
  45. Dummy Object • Stand-in for the real dependency • Does

    not any functionality • Only needs to ‘look like’ the real dependency
  46. Dummy Object $mock = Mockery::mock(‘Grumpy\Foo’);

  47. Stubs • ‘Dummy object’ but with defined methods • Methods

    don’t need to return anything • Satisfies any calls to the dependency where the response doesn’t matter
  48. Stubs $mock = Mockery::mock(‘Grumpy\Foo’); $mock->shouldReceive(‘bar’);

  49. Mocks • ‘Stub’ where return value for methods are set

    • Most common test double you will use
  50. Mocks $mock = Mockery::mock(‘Grumpy\Foo’); $mock->shouldReceive(‘bar’) ->andReturn(false);

  51. Test Doubles Considered Harmful • Be careful to not fall

    in love with test doubles • Having to create too many of them exposes tightly- coupled code
  52. Test Doubles Considered Harmful • Use them when you have

    a dependency that is difficult to use under normal circumstances • Database connections and 3rd party API calls come to mind
  53. Code Kata I • Code katas are small coding exercises

    with known solutions • Designed to turn certain programming practices into “muscle memory” • Concept taken from Asian martial arts
  54. Code Kata I • FizzBuzz! • great exercise for covering

    programming basics • easily tested
  55. Code Kata I • make sure you create a directory

    to do your exercises in • make sure you have Composer installed • make sure you’ve installed PHPUnit using it
  56. FizzBuzz • Take a collection of integers • If the

    integer is divisible by 3, change it to ‘Fizz’ • If the integer is divisible by 5, change it to ‘Buzz’ • If the integer is divisible by 3 and 5, change it to ‘FizzBuzz’ • Otherwise do not change the value
  57. FizzBuzz

  58. FizzBuzz

  59. FizzBuzz

  60. FizzBuzz

  61. FizzBuzz

  62. FizzBuzz

  63. FizzBuzz

  64. FizzBuzz

  65. FizzBuzz

  66. FizzBuzz

  67. FizzBuzz

  68. Data Providers • Reduce the number of tests you write

    • Modify test data sets without modifying test
  69. Data Providers • Modify test method to accept parameters matching

    the data you will provide • Create a method that returns an array of arrays containing data
  70. Data Providers

  71. Data Providers

  72. Data Providers

  73. Code Kata II • Your turn to do some TDD!

    • Create an object that turns arabic numbers into Roman Numerals
  74. Code Kata II • Use TDD to design your class

    • Use data providers • Get into writing code in an iterative way
  75. Code Kata II 1 -> I 2 -> II 3

    -> III 4 -> IV 5 -> V 6 -> VI 7 -> VII 8 -> VIII 9 -> IX 10 -> X 40 -> XL 50 -> L
  76. Test Doubles In Action • Replacing “real” dependencies with ones

    we place in a specific state • Allows us to write tests for code that speaks to data sources and web services
  77. Test Doubles In Action

  78. Test Doubles In Action

  79. Test Doubles In Action

  80. Test Doubles In Action

  81. A Simple Example

  82. A Simple Example

  83. A Simple Example

  84. A Simple Example

  85. Code Kata III • Use TDD to add a method

    called getAllActive() • Uses fetchAll() to get back a data set that includes id, email, and is_active set to 1 or 0 • Have at least 3 records, with 2 active • You must manually filter out records in getAllActive() • return results as array with just ‘id’ and ‘email’
  86. Code Kata III

  87. Code Kata III

  88. Code Kata IV • Time to tie it all together!

    • Create some doubles • Create some assertions • Write a “working” app the TDD way
  89. Code Kata IV • Let’s create WidgetTrend • Hit a

    remote API that gives us the price of various widgets in real time • Store the widget prices in a database
  90. Code Kata IV{name of widget}

  91. Code Kata IV Save info in a table called ‘widgettracker’

    id | name | price | date
  92. Code Kata IV • write a test that uses an

    object that calls the API and then stores the value in the database • you will need a test double for the class that calls the API • you will need a test double for the class that stores information in the database
  93. Code Kata IV • Remember to start off with a

    test that assumes your code is working • Don’t be afraid to explore things until you’ve decided what your code’s interfaces look like. Prototyping is not the time for tests! • Focus on things one test at a time, don’t look ahead to future functionality. Your tests will let you go back and rework things and tell you if you broke something in the process
  94. Just The Beginning • Learn the basics • Get into

    the TDD rhythm • Dig into other testing tools and techniques
  95. Thank you! Question time? https://joind.in/talk/62de8