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!
Who Should Test? • developers looking for stable deployments • developers looking to increase ability to change • developers looking to go home on time
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
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
Done By Developers • Cheapest time to find bugs is during development • TDD is an incredibly effective design process • Studies have proven it’s effectiveness
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
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
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
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
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
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
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?
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
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
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
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
Constructor Injection • Pass in dependencies at object creation • Gets messy if many dependencies are required • Can lead to __construct() doing too much work
Setter Injection • “Less messy” than using constructors • Refactoring to add get/set methods not overly intrusive • Allows overriding of internally-created dependencies
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
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
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
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
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
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
Test Doubles • Understanding them was the most difficult thing I had to learn • Makes you understand how critical putting dependencies in specific states is
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
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
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’
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
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
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