Unfortunately many developers still struggle with testing. Presentation goal is to introduce basic testing terminology and tools, to show and encourage developers how to test there code. All examples and tools are for PHP (PhpUnit & Prophecy)
KENT BECK'S RULES OF SIMPLE CODE 1. Runs all the tests 2. Contains no duplication 3. Express the intent of programmer 4. Minimizes the number of classes and methods Given in order of importance
WHAT TESTING REALLY IS? Prove you've done your work Reduce number of bugs Facilitate change Help you make better code design - easier to maintain Ensures stable, long lasting application Improve understanding how clients are going to use your code Enhance security Provide free documentation
AUTOMATED TESTING When I say testing I mean AUTOMATED testing Manual testing is time consuming and boring Automated testing is fun and a key point for software quality
TEST TYPES Acceptance (end-to-end) Test as if end user would use the whole system/feature Integration Test how different parts of system work together Unit Test single unit of code - mock all dependencies
WHAT IS UNIT TESTING? A software testing method to test individual unit of source code - a method in class or a piece of code The purpose of unit testing is not for finding bugs Specification for the expected behaviours of the code under test
UNIT TESTING FIRST PRINCIPLES Fast - Be fast or be dead Independent/Isolated - Run without dependency Repeatable - Should be idempotent Self-verifying - Just pass or fail Timely - Write test before production
WRITE UNIT TESTS BEFORE OR AFTER CODE? After - You test what you code - Test Last Development Driven Testing - DDT Before - You code what you test - Test First Test Driven Development - TDD
THE THREE LAWS OF TDD 1. You may not write production code until you have written a failing unit test 2. You may not write more of a unit test than is sufficient to fail 3. You may not write more production code than is sufficient to pass the currently failing test
Member of xUnit family Created by Sebastian Bergmann and contributors Integrated/Supported by all modern frameworks Integrated in most IDE (PHPStorm, Netbeans, Eclipse, ZS) Written for PHP 5.x Install using composer or phar "require-dev": { "phpunit/phpunit": "4.*" }, PEAR install - not supported from 1.1.2015
1. TEST RUNNER Executable program that runs tests implemented using an xUnit framework and reports the test results PhpUnit - phpunit/phpunit/phpunit file
3. TEST FIXTURES Set of preconditions or state needed to run a test Known good state should be set up before the tests, and return to the original state after the tests PhpUnit - setUp(), tearDown(), setUpBeforeClass() and tearDownAfterClass() methods protected function setUp() { // preconditions or DRY code } protected function tearDown() { // clear all after tests }
6. TEST RESULT FORMATTER Produces results in one or more output formats PhpUnit test results: . - Test succeeds F - Assertion fails while running the test method E - Error occurs while running the test method R - Test has been marked as risky S - Test has been skipped I - Test is marked as being incomplete or not yet implemented
7. ASSERTIONS Function that verifies the behavior (or the state) of the unit under test Usually result is bool PhpUnit examples assertTrue - Check the input to verify it equals true assertFalse - Check the input to verify it equals false assertEquals - Check the result against another input for a match assertContains - Check that the input contains a certain value
Bootstrap example (Laravel) Runs before all tests require __DIR__.'/vendor/autoload.php'; date_default_timezone_set('UTC'); Carbon\Carbon::setTestNow(Carbon\Carbon::now());
TEST STRUCTURE The tests for a class Class go into a class ClassTest ClassTest inherits from PHPUnit_Framework_TestCase The tests are public methods that are named test* Inside the test methods assertion methods are used - assert*
PhpUnit test example (Laravel ConsoleScheduledEventTest) // Some parts of code removed intentionaly class ConsoleScheduledEventTest extends PHPUnit_Framework_TestCase { /** * The default configuration timezone. * * @var string */ protected $defaultTimezone; public function setUp() { $this->defaultTimezone = date_default_timezone_get(); date_default_timezone_set('UTC'); } public function tearDown()
PROPHECY Highly opinionated mocking framework for PHP 5.3+ Created by Konstantin Kudryashov and contributors Initially it was created to fulfil phpspec2 needs From version 4.5 PHPUnit is the out-of-the-box support for Prophecy Developers will be encouraged to use Prophecy instead of the PHPUnit mocks Composer install "require-dev": { "phpspec/prophecy": "~1.0" },
Every word has a logical meaning, even the name of the library itself (Prophecy) “Prophecy has been named that way because it concentrates on describing the future behavior of objects with very limited knowledge about them. But as with any other prophecy, those object prophecies can't create themselves - there should be a Prophet” $prophet = new Prophecy\Prophet;
“The prophet creates prophecies by prophesizing them. Test double objects are then created by revealing their respective prophecy. You can think of a prophecy as the configuration for a test double that is stored in an object separate from the object (revelation) that acts as the test double.” Confused? Show me some examples
FAKE The output of the method isn’t important Prophecy will not tolerate faking a method that hasn’t been declared anywhere class MarkdownTest extends TestCase { /** @test */ function it_attaches_default_events() { $eventDispatcher = $this->prophesize('Markdown\Event\EventDispatcher' $eventDispatcher->addListener(Argument::type('Markdown\Event\EndOfLineL $markdown = new Markdown($eventDispatcher->reveal()); // continue with some assertions on $markdown... } }
interface PrinterInterface { public function isAvailable(); } class Printer implements PrinterInterface { public function isAvailable() { // some code goes here } } class FakePrinter implements PrinterInterface { public function isAvailable() { return true;
STUB Has (basic) behaviour but no expectations It does not matter how many times a method is called - Doesn't have to be called Promises it will always return the same thing (willReturn())
class CustomerCardPrinter { private $printer; private $customer; public function __construct(PrinterInterface $printer, CustomerInterface { $this->printer = $printer; $this->customer = $customer; } public function print() { if (! $this->printer->isAvailable()){ return false; }
MOCK Define predictions (shouldBeCalled()), not promises Describing what will happen in the future Keeps track of method calls and their arguments Validates method calls given a certain set of expectations
class CustomerCardPrinter { private $printer; private $customer; public function __construct(PrinterInterface $printer, CustomerInterface { $this->printer = $printer; $this->customer = $customer; } public function print() { if (! $this->printer->isAvailable()){ return false; }
Once you start testing You wont want to write code without tests again You are not good developer if you are not good tester Writing code that is testable encourages best practices - such as SOLID Quality is everyone’s responsibility — especially ours - developer’s Testing is fun :-)
Php tests tips Unit test prez Cost of tdd Sebastian Bergmann in Atlanta F.I.R.S.T Principles of Unit Testing First principles Prag prog blog How Often Should You Run Your JUnit Tests? Daed blog Salesforce blog Google testing blog Unit testing blog Testing doubles Martin Fowler about XUnit Notes on designing through mocking
IMAGES We are busy Vagrant & Docker TDD lifecycle Testing code in production Hard work Steep learning curve Advice True or false Testing pyramid Inverted Testing pyramid Hourglass Kohana testing structure Facts