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



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)


Milan Popović

August 18, 2015

More Decks by Milan Popović

Other Decks in Programming


  1. What is main property of bad code? IT'S UNTESTABLE


    tests 2. Contains no duplication 3. Express the intent of programmer 4. Minimizes the number of classes and methods Given in order of importance
  3. TESTIMONY Presented by / Milan Popović @komita1981

  4. ME PHP developer Work for Nano Interactive I like to

    learn and share knowledge Active member of PHP Srbija
  5. WHAT IS TESTING? Testing is the activity of finding out

    whether a piece of code produces the intended behavior

  7. None
  8. None
  9. None
  10. None
  11. None
  12. Time consuming Testing is hard Steep learning curve Don’t make

    anybody any money
  13. 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
  14. 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
  15. 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
  16. None
  17. GOOGLE TEAM SUGGEST E2E - 10 % Integration - 20%

    Unit - 70%

  19. Inverted pyramid/ice cream cone

  20. Hourglass

  21. 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
  22. What is to be tested? EVERYTHING To be more precise

  23. WHAT DOES NOT NEED TO BE TESTED? Getters/Setters Framework Third

    party packages Protected & private methods and properties
  24. 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

    test what you code - Test Last Development Driven Testing - DDT Before - You code what you test - Test First Test Driven Development - TDD
  26. None
  27. 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
  28. None
  29. IBM & Microsoft - TDD - 20-40% longer to complete

    but 40- 90% fewer bugs in production
  30. By 2022 it will be not be possible to get

    a professional programming job if you do not practice TDD routinely - Allan Kelly
  31. None
  32. 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
  33. XUNIT ARCHITECTURE 1. Test runner 2. Test case 3. Test

    fixtures 4. Test suites 5. Test executions 6. Test result formatter 7. Assertions
  34. 1. TEST RUNNER Executable program that runs tests implemented using

    an xUnit framework and reports the test results PhpUnit - phpunit/phpunit/phpunit file
  35. 2. TEST CASE The most elemental class All unit tests

    are inherited from this class PhpUnit - PHPUnit_Framework_TestCase class
  36. 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 }
  37. 4. TEST SUITES Set of tests that all share the

    same fixture PhpUnit - testsuites options in phpunit.xml // part of phpunit.xml file <testsuite name="Unit"> <directory>./app/tests/Unit/</directory> </testsuite> // from the command line phpunit --testsuite Unit
  38. 5. TEST EXECUTION setUp(); ... /* Body of test -

    Tested class - System Under Tests */ ... tearDown(); */
  39. 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
  40. 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
  41. Many more asserts - official documentation...

  42. PHPUNIT'S TEST GOALS Easy to learn to write Easy to

    write Easy to read Easy to execute Quick to execute Isolated Composable
  43. TEST CONFIG Configured in phpunit.xml file <phpunit bootstrap="bootstrap/autoload.php" backupglobals="false" backupstatica

    <testsuites> <testsuite name="Unit"> <directory>./app/tests/Unit/</directory> </testsuite> <testsuite name="Integration"> <directory>./app/tests/Integration/</directory> </testsuite> </testsuites> </phpunit>
  44. Bootstrap example (Laravel) Runs before all tests require __DIR__.'/vendor/autoload.php'; date_default_timezone_set('UTC');

  45. None
  46. 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*
  47. 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()
  48. TESTING DOUBLES Dummy Fake Stub Mock Spy

  49. GENERATION DOUBLES DOUBLES phpunit/phpunit-mock-object phpspec/prophecy mockery/mockery phake/phake codeception/aspect-mock php-vci/php-vci

  50. 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" },
  51. 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;
  52. “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
  53. DUMMY Objects are passed around but never actually used Passed

    around for typehinting No behaviour
  54. 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; } $customerName = $customer->getName();
  55. 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... } }
  56. 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;
  57. 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())
  58. 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; }
  59. 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
  60. 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; }
  61. SPY Records behaviour Describing what has happened in the past

    Assertions happen afterwards (shouldHaveBeenCalled())
  62. function it_notifies_an_attached_subscriber() { $parser = new ParserSubject; $dummyEvent = $this->prophesize('Markdown\Event\Event')->reveal();

    $subscriber = $this->prophesize('Markdown\Parser\Subscriber')->reveal $parser->notify($dummyEvent); $subscriber->onChange($dummyEvent)->shouldHaveBeenCalled(); }
  63. 3 A OF UNIT TESTING Arrange objects, creating and setting

    them up as necessary. Act on an object Assert that something is as expected
  64. Arrange = Setup Act = Poke Assert = Verify

  65. public function testSuccessful() { // ARRANGE $someDriverProphet = $this->prophesize('\SomeNamespace\SomePackage\SomeDriv $someDriverProphet->doSomething()->shouldBeCalled()->willReturn('value'

    $testedClass = new TestedClass($someDriverProphet); // ACT $testResult = $testedClass->testedMethod(); // ASSERT $this->assertSomething($expectedResult, $testResult); }
  66. STRUCTURING UNIT TESTS One test class per tested class One

    test class per method of tested class
  67. None
  68. 1. Try with writing tests after writing the code

  69. 2. When you get confidence try to write tests before

    the code
  70. 3. Treat test like your code - keep it "clean"

    Be descriptive about what you are testing Do not duplicate test code Do not make tests complex
  71. 4. Number of asserts in a test ought to be

  72. 5. Use Virtual Environments

  73. 6. Run tests often

  74. 7. Test Boundary Conditions

  75. 8. Use test saboteurs (Humbug project)

  76. 9. Use test coverage

  77. Test branch coverage not only code coverage sampleMethod() { if

    (a < 5 and b > 10){ return a + b; } return $a - $b; }
  78. 10. Test before refactoring

  79. 11. Write unit tests for each bug you are going

    to fix
  80. 12. Think twice before using New Operators - new ClassName()

  81. 13. Think twice before using statics - SomeClass::someMethod()

  82. 14. Avoid endless "anding" - Breaking SRP

  83. 15. Avoid using switch-case often - use design patterns instead

  84. 16. Avoid using too many dependencies - max 4 dependencies

  85. 17. Avoid logic in constructor - only assign variables

  86. 17. Do not break Law of Demeter if ($account->getUser()->getRole()->IsAdmin()) {

    return false; }
  87. 18. Don’t Mock What You Don’t Own

  88. 19. Use data providers /** * @dataProvider getSuccessfulAddData */ public

    function testSuccessfulAdd($a, $b, $result) { $this->assertEquals($result, $this->calculator->add($a, $b)); } public function getSuccessfulAddData() { return [ ['a' => 1, 'b' => 2, 'result' => 3], ['a' => 2, 'b' => 1, 'result' => 3], ['a' => 0, 'b' => 1, 'result' => 1], ['a' => 1, 'b' => 0, 'result' => 1], ];
  89. 20. Use continuous integration server

  90. None
  91. 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 :-)
  92. None
  93. None
  94. None
  95. None

  97. 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
  98. 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