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 full-size 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 full-size slide

  3. TESTIMONY
    Presented by /
    Milan Popović @komita1981

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  6. WHAT DO PEOPLE THINK ABOUT
    TESTING?

    View full-size slide

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

    View full-size slide

  8. 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 full-size slide

  9. 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 full-size slide

  10. 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 full-size slide

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

    View full-size slide

  12. TESTING ANTI-PATTERNS

    View full-size slide

  13. Inverted pyramid/ice cream cone

    View full-size slide

  14. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  17. 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 full-size slide

  18. 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 full-size slide

  19. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

  22. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  26. 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 full-size slide

  27. 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 full-size slide

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

    View full-size slide

  29. 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 full-size slide

  30. 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 full-size slide

  31. Many more asserts - official documentation...

    View full-size slide

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

    View full-size slide

  33. TEST CONFIG
    Configured in phpunit.xml file


    ./app/tests/Unit/


    ./app/tests/Integration/



    View full-size slide

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

    View full-size slide

  35. 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 full-size slide

  36. 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 full-size slide

  37. TESTING DOUBLES
    Dummy
    Fake
    Stub
    Mock
    Spy

    View full-size slide

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

    View full-size slide

  39. 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 full-size slide

  40. 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 full-size slide

  41. “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 full-size slide

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

    View full-size slide

  43. 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 full-size slide

  44. 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 full-size slide

  45. 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 full-size slide

  46. 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 full-size slide

  47. 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 full-size slide

  48. 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 full-size slide

  49. 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 full-size slide

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

    View full-size slide

  51. 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 full-size slide

  52. 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 full-size slide

  53. Arrange = Setup
    Act = Poke
    Assert = Verify

    View full-size slide

  54. 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 full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  58. 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 full-size slide

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

    View full-size slide

  60. 5. Use Virtual Environments

    View full-size slide

  61. 6. Run tests often

    View full-size slide

  62. 7. Test Boundary Conditions

    View full-size slide

  63. 8. Use test saboteurs (Humbug project)

    View full-size slide

  64. 9. Use test coverage

    View full-size slide

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

    View full-size slide

  66. 10. Test before refactoring

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

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

    View full-size slide

  76. 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 full-size slide

  77. 20. Use continuous integration server

    View full-size slide

  78. 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 full-size slide

  79. 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 full-size slide

  80. 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 full-size slide