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

Testimony

 Testimony

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
Tweet

More Decks by Milan Popović

Other Decks in Programming

Transcript

  1. 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
  2. ME PHP developer Work for Nano Interactive I like to

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

    whether a piece of code produces the intended behavior
  4. 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
  5. 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
  6. 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
  7. 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
  8. What is to be tested? EVERYTHING To be more precise

    EVERYTHING THAT COULD BREAK YOUR APP
  9. WHAT DOES NOT NEED TO BE TESTED? Getters/Setters Framework Third

    party packages Protected & private methods and properties
  10. 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
  11. 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
  12. 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
  13. IBM & Microsoft - TDD - 20-40% longer to complete

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

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

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

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

    are inherited from this class PhpUnit - PHPUnit_Framework_TestCase class
  19. 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 }
  20. 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
  21. 5. TEST EXECUTION setUp(); ... /* Body of test -

    Tested class - System Under Tests */ ... tearDown(); */
  22. 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
  23. 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
  24. PHPUNIT'S TEST GOALS Easy to learn to write Easy to

    write Easy to read Easy to execute Quick to execute Isolated Composable
  25. 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>
  26. 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*
  27. 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()
  28. 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" },
  29. 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;
  30. “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
  31. 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();
  32. 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... } }
  33. 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;
  34. 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())
  35. 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; }
  36. 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
  37. 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; }
  38. SPY Records behaviour Describing what has happened in the past

    Assertions happen afterwards (shouldHaveBeenCalled())
  39. 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(); }
  40. 3 A OF UNIT TESTING Arrange objects, creating and setting

    them up as necessary. Act on an object Assert that something is as expected
  41. 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); }
  42. STRUCTURING UNIT TESTS One test class per tested class One

    test class per method of tested class
  43. 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
  44. Test branch coverage not only code coverage sampleMethod() { if

    (a < 5 and b > 10){ return a + b; } return $a - $b; }
  45. 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], ];
  46. 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 :-)
  47. 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
  48. 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