$30 off During Our Annual Pro Sale. View Details »

Learn To Test Like A Grumpy Programmer

Learn To Test Like A Grumpy Programmer

Slides for my workshop at PHPBenelux 2017

Chris Hartjes

January 27, 2017
Tweet

More Decks by Chris Hartjes

Other Decks in Technology

Transcript

  1. Learn To Test Like A
    Grumpy Programmer
    Chris Hartjes
    PHPBenelux Conference 2017

    View Slide

  2. Who is this guy
    and why
    should I listen?
    • Long-time tester
    • Beard conveys authority
    • Twitter account is verified
    • PHP dev since 1998
    • Wants to help you get
    better!

    View Slide

  3. 5 W’s
    (and one H)

    View Slide

  4. Who Should Test?
    • developers looking for stable deployments
    • developers looking to increase ability to change
    • developers looking to go home on time

    View Slide

  5. Stable Deployments
    AUTOMATE
    AUTOMATE
    AUTOMATE

    View Slide

  6. Stable Deployment
    • Exposes poor deployment practices
    • Exposes brittle application architectures
    • Promotes “automation-first” thinking

    View Slide

  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?

    View Slide

  8. Confidence In Changes
    • Removing technical debt is always scary
    • Increases reliance on automated systems
    • Forces thinking about more than just code

    View Slide

  9. Go Home On Time
    • Production pushes will go smoother
    • Eliminates “stick around just in case” deployments
    • Makes deployments a non-issue

    View Slide

  10. What Is TDD?
    • Design Tool
    • Style Enforcer
    • Hard-to-shake Coding Habit

    View Slide

  11. Design Tool
    • Tests as working examples of code
    • Forces you to concentrate on interfaces and API’s

    View Slide

  12. Design Tool
    • Limits work in progress
    • Exposes awkward code earlier
    • Exposes dependencies for code earlier

    View Slide

  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

    View Slide

  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?”

    View Slide

  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

    View Slide

  16. Where Do You Write Tests?
    • In development environments
    • As part of the NORMAL development process
    • When not prototyping

    View Slide

  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

    View Slide

  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

    View Slide

  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)

    View Slide

  20. “For just one extra day a week you can have
    90% fewer bugs show up in production!”

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  24. Opportunity Costs
    X vs. 10X vs. 1000X

    View Slide

  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

    View Slide

  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

    View Slide

  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?

    View Slide

  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

    View Slide

  29. Building Blocks
    It’s Like Using Lego

    View Slide

  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

    View Slide

  31. Assertions
    • The backbone of unit testing
    • Simple to understand

    View Slide

  32. Assertions
    • Unit tests (usually) usually have one or more
    assertions
    • Proves that your expectation of an outcome is
    correct

    View Slide

  33. The Simplest Test That Runs

    View Slide

  34. Assertions
    • $this->assertTrue()
    • $this->assertFalse()
    • $this->assertEquals(,

    View Slide

  35. Assertions
    Those three
    assertions will cover
    99% of your unit
    testing needs

    View Slide

  36. 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

    View Slide

  37. Dependency Injection
    Techniques
    • Globally-available containers
    • Constructor injection
    • Setter injection

    View Slide

  38. 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

    View Slide

  39. Constructor Injection
    • Pass in dependencies at object creation
    • Gets messy if many dependencies are required
    • Can lead to __construct() doing too much work

    View Slide

  40. Setter Injection
    • “Less messy” than using constructors
    • Refactoring to add get/set methods not overly
    intrusive
    • Allows overriding of internally-created
    dependencies

    View Slide

  41. Types Of Test Doubles
    • Classical definition is that there are five types
    • Dummy objects, test stubs, test spies, test mocks,
    test fakes

    View Slide

  42. Types Of Test Doubles
    • PHPUnit-compatible test double tools tend to only
    use three
    • Dummy objects, test stubs, test mocks

    View Slide

  43. Dummy Object
    • Stand-in for the real dependency
    • Does not any functionality
    • Only needs to ‘look like’ the real dependency

    View Slide

  44. Dummy Object
    $mock = Mockery::mock(‘Grumpy\Foo’);

    View Slide

  45. 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

    View Slide

  46. Stubs
    $mock = Mockery::mock(‘Grumpy\Foo’);
    $mock->shouldReceive(‘bar’);

    View Slide

  47. Mocks
    • ‘Stub’ where return value for methods are set
    • Most common test double you will use

    View Slide

  48. Mocks
    $mock = Mockery::mock(‘Grumpy\Foo’);
    $mock->shouldReceive(‘bar’)
    ->andReturn(false);

    View Slide

  49. 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

    View Slide

  50. 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

    View Slide

  51. 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

    View Slide

  52. Code Kata I
    • FizzBuzz!
    • great exercise for covering programming basics
    • easily tested

    View Slide

  53. 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

    View Slide

  54. 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

    View Slide

  55. FizzBuzz

    View Slide

  56. FizzBuzz

    View Slide

  57. FizzBuzz

    View Slide

  58. FizzBuzz

    View Slide

  59. FizzBuzz

    View Slide

  60. FizzBuzz

    View Slide

  61. FizzBuzz

    View Slide

  62. FizzBuzz

    View Slide

  63. FizzBuzz

    View Slide

  64. FizzBuzz

    View Slide

  65. FizzBuzz

    View Slide

  66. Data Providers
    • Reduce the number of tests you write
    • Modify test data sets without modifying test

    View Slide

  67. 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

    View Slide

  68. Data Providers

    View Slide

  69. Data Providers

    View Slide

  70. Data Providers

    View Slide

  71. Code Kata II
    • Your turn to do some TDD!
    • Create an object that turns arabic numbers into
    Roman Numerals

    View Slide

  72. Code Kata II
    • Use TDD to design your class
    • Use data providers
    • Get into writing code in an iterative way

    View Slide

  73. 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

    View Slide

  74. Test Doubles
    • Understanding them was the most difficult thing I
    had to learn
    • Makes you understand how critical putting
    dependencies in specific states is

    View Slide

  75. 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

    View Slide

  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

    View Slide

  77. A Simple Example

    View Slide

  78. A Simple Example

    View Slide

  79. A Simple Example

    View Slide

  80. A Simple Example

    View Slide

  81. 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’

    View Slide

  82. Code Kata III

    View Slide

  83. Code Kata III

    View Slide

  84. Code Kata IV
    • Time to tie it all together!
    • Create some doubles
    • Create some assertions
    • Write a “working” app the TDD way

    View Slide

  85. 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

    View Slide

  86. Code Kata IV
    http://127.0.0.1/api/widget/{name of widget}

    View Slide

  87. Code Kata IV
    Save info in a table called ‘widgettracker’
    id | name | price | date

    View Slide

  88. 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

    View Slide

  89. 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

    View Slide

  90. Just The Beginning
    • Learn the basics
    • Get into the TDD rhythm
    • Dig into other testing tools and techniques

    View Slide

  91. Thank you!
    Question time?

    View Slide