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. What is main property of bad code?
    IT'S UNTESTABLE

    View Slide

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

    View Slide

  3. TESTIMONY
    Presented by /
    Milan Popović @komita1981

    View Slide

  4. ME
    PHP developer
    Work for Nano Interactive
    I like to learn and share knowledge
    Active member of PHP Srbija

    View Slide

  5. WHAT IS TESTING?
    Testing is the activity of finding out whether a piece of code
    produces the intended behavior

    View Slide

  6. WHAT DO PEOPLE THINK ABOUT
    TESTING?

    View Slide

  7. View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. View Slide

  12. Time consuming
    Testing is hard
    Steep learning curve
    Don’t make anybody any money

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  16. View Slide

  17. GOOGLE TEAM SUGGEST
    E2E - 10 %
    Integration - 20%
    Unit - 70%

    View Slide

  18. TESTING ANTI-PATTERNS

    View Slide

  19. Inverted pyramid/ice cream cone

    View Slide

  20. Hourglass

    View Slide

  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

    View Slide

  22. What is to be tested?
    EVERYTHING
    To be more precise
    EVERYTHING THAT COULD BREAK
    YOUR APP

    View Slide

  23. WHAT DOES NOT NEED TO BE
    TESTED?
    Getters/Setters
    Framework
    Third party packages
    Protected & private methods and properties

    View Slide

  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

    View Slide

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

    View Slide

  26. View Slide

  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

    View Slide

  28. View Slide

  29. IBM & Microsoft - TDD - 20-40% longer to complete but 40-
    90% fewer bugs in production

    View Slide

  30. By 2022 it will be not be possible to get a
    professional programming job if you do not
    practice TDD routinely - Allan Kelly

    View Slide

  31. View Slide

  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

    View Slide

  33. XUNIT ARCHITECTURE
    1. Test runner
    2. Test case
    3. Test fixtures
    4. Test suites
    5. Test executions
    6. Test result formatter
    7. Assertions

    View Slide

  34. 1. TEST RUNNER
    Executable program that runs tests implemented using an
    xUnit framework and reports the test results
    PhpUnit - phpunit/phpunit/phpunit file

    View Slide

  35. 2. TEST CASE
    The most elemental class
    All unit tests are inherited from this class
    PhpUnit - PHPUnit_Framework_TestCase class

    View Slide

  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
    }

    View Slide

  37. 4. TEST SUITES
    Set of tests that all share the same fixture
    PhpUnit - testsuites options in phpunit.xml
    // part of phpunit.xml file

    ./app/tests/Unit/

    // from the command line
    phpunit --testsuite Unit

    View Slide

  38. 5. TEST EXECUTION
    setUp();
    ...
    /* Body of test - Tested class - System Under Tests */
    ...
    tearDown(); */

    View Slide

  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

    View Slide

  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

    View Slide

  41. Many more asserts - official documentation...

    View Slide

  42. PHPUNIT'S TEST GOALS
    Easy to learn to write
    Easy to write
    Easy to read
    Easy to execute
    Quick to execute
    Isolated
    Composable

    View Slide

  43. TEST CONFIG
    Configured in phpunit.xml file


    ./app/tests/Unit/


    ./app/tests/Integration/



    View Slide

  44. Bootstrap example (Laravel)
    Runs before all tests
    require __DIR__.'/vendor/autoload.php';
    date_default_timezone_set('UTC');
    Carbon\Carbon::setTestNow(Carbon\Carbon::now());

    View Slide

  45. View Slide

  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*

    View Slide

  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()

    View Slide

  48. TESTING DOUBLES
    Dummy
    Fake
    Stub
    Mock
    Spy

    View Slide

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

    View Slide

  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"
    },

    View Slide

  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;

    View Slide

  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

    View Slide

  53. DUMMY
    Objects are passed around but never actually used
    Passed around for typehinting
    No behaviour

    View Slide

  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();

    View Slide

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

    View Slide

  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;

    View Slide

  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())

    View Slide

  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;
    }

    View Slide

  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

    View Slide

  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;
    }

    View Slide

  61. SPY
    Records behaviour
    Describing what has happened in the past
    Assertions happen afterwards (shouldHaveBeenCalled())

    View Slide

  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();
    }

    View Slide

  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

    View Slide

  64. Arrange = Setup
    Act = Poke
    Assert = Verify

    View Slide

  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);
    }

    View Slide

  66. STRUCTURING UNIT TESTS
    One test class per tested class
    One test class per method of tested class

    View Slide

  67. View Slide

  68. 1. Try with writing tests after writing the code

    View Slide

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

    View Slide

  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

    View Slide

  71. 4. Number of asserts in a test ought to be minimized

    View Slide

  72. 5. Use Virtual Environments

    View Slide

  73. 6. Run tests often

    View Slide

  74. 7. Test Boundary Conditions

    View Slide

  75. 8. Use test saboteurs (Humbug project)

    View Slide

  76. 9. Use test coverage

    View Slide

  77. Test branch coverage not only code coverage
    sampleMethod()
    {
    if (a < 5 and b > 10){
    return a + b;
    }
    return $a - $b;
    }

    View Slide

  78. 10. Test before refactoring

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  87. 18. Don’t Mock What You Don’t Own

    View Slide

  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],
    ];

    View Slide

  89. 20. Use continuous integration server

    View Slide

  90. View Slide

  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 :-)

    View Slide

  92. View Slide

  93. View Slide

  94. View Slide

  95. View Slide

  96. RESOURCES

    View Slide

  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

    View Slide

  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

    View Slide

  99. THANK YOU

    View Slide

  100. QUESTIONS?

    View Slide