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

Unit Testing

Unit Testing

Unit testing talk for Twilio

Bulat Shakirzyanov

September 26, 2011
Tweet

More Decks by Bulat Shakirzyanov

Other Decks in Programming

Transcript

  1. Unit testing
    A guide to writing clean, testable code,
    that will be easy to maintain and extend
    Monday, September 26, 11

    View Slide

  2. • @avalanche123
    • github.com/avalanche123
    • avalanche123.com
    Bulat Shakirzyanov
    Monday, September 26, 11

    View Slide

  3. •Unit testing
    •Dependency injection
    •Mock objects
    •Test driven development
    •Clean code techniques
    Agenda
    Monday, September 26, 11

    View Slide

  4. •End to end, black box testing -
    tests are hardest to write and
    run, tests find bugs that are
    easiest to fix (bad markup,
    wrong css rule)
    Types of tests
    Monday, September 26, 11

    View Slide

  5. •Functional, wiring tests -
    easier to write tests, find
    easy to medium bugs (cannot
    connect to database, because
    server is misconfigured)
    Types of tests
    Monday, September 26, 11

    View Slide

  6. Types of tests
    •Unit tests - easiest to write,
    fix logic bugs and hard-to-spot
    design problems (violation of
    single responsibility
    principle, hard-coded function
    calls)
    Monday, September 26, 11

    View Slide

  7. “Unit testing is a method by which individual units
    of source code are tested to determine if they are fit for
    use. A unit is the smallest testable part of an application.”
    wikipedia.org
    Monday, September 26, 11

    View Slide

  8. Why and why not unit test
    To test or not to test?
    Monday, September 26, 11

    View Slide

  9. •Debugging is a time consuming process
    •When new functionality is added, how
    do we make sure the old one doesn't
    break
    •By looking at a unit test, you can
    see the class in action, which lets
    you easily understand its intent and
    proper use
    •Unit tests are the only real measure
    of project health and code quality
    Reasons
    Monday, September 26, 11

    View Slide

  10. •I never make mistakes
    •The functionality is trivial
    •Tests slow me down
    •Management won't let me
    Excuses
    Monday, September 26, 11

    View Slide

  11. Facts
    •I don't know how to test
    Monday, September 26, 11

    View Slide

  12. Properties of a unit test
    Recognize
    Monday, September 26, 11

    View Slide

  13. •Isolated
    •Repeatable
    •Fast
    •Self-documenting
    Unit test is...
    Monday, September 26, 11

    View Slide

  14. •No need to build a car to test
    tires
    •Testing payment gateway should
    not affect my monthly statement
    •It runs faster
    Test isolation
    Monday, September 26, 11

    View Slide

  15. •Building cars to test tires is
    a waste of time and money
    Test isolation
    Monday, September 26, 11

    View Slide

  16. •Tests need to be run by every
    developer, no matter what stack
    he/she uses
    •Tests must not rely on the
    environment in which they are
    being run
    Repeatability
    Monday, September 26, 11

    View Slide

  17. •Lower the project entry costs/
    barriers
    Repeatability
    Monday, September 26, 11

    View Slide

  18. •Time is money, the longer we
    wait for tests to run, the
    more money our clients or
    company loses
    Speed
    Monday, September 26, 11

    View Slide

  19. •Testable code is clear and easy
    to follow
    •No need to explain how a
    certain component works, they
    can just look at the test
    •No need to write documentation
    Self-documentation
    Monday, September 26, 11

    View Slide

  20. •We usually look for usage
    examples in documentation
    anyway
    Self-documentation
    Monday, September 26, 11

    View Slide

  21. •Isolate
    •Reproduce
    •Speed-up
    •Document
    TODO
    Monday, September 26, 11

    View Slide

  22. Decouple components
    Property 1: Isolation
    Monday, September 26, 11

    View Slide

  23. “[dependency injection is] … a technique for
    decoupling highly dependent software
    components”
    wikipedia.org
    Monday, September 26, 11

    View Slide

  24. •Can't use a test
    version of
    PaymentGateway
    •This test will end
    up in my monthly
    statement
    Hidden dependency
    class BankAccount
    {
    //...
    public function charge($amount)
    {
    $gateway = new PaymentGateway();
    // or
    $gateway = PaymentGateway::getInstance();
    // or
    $gateway = Registry::get('payment_gateway');
    //...
    $gateway->charge($amount, $this);
    }
    }
    Monday, September 26, 11

    View Slide

  25. •PaymentGateway can
    now be replaced
    •BankAccount
    shouldn't be
    responsible for
    charging itself
    Injected dependency
    class BankAccount
    {
    //...
    public function charge($amount, PaymentGateway $g)
    {
    $g->charge($amount, $this);
    }
    }
    Monday, September 26, 11

    View Slide

  26. •Makes BankAccount
    class lighter,
    leaving it only
    responsibility of
    storing account
    balance
    Single responsibility
    class BankAccount
    {
    //...
    }
    $gateway = new PaymentGateway();
    $account = new BankAccount();
    $gateway->charge(100, $account);
    Monday, September 26, 11

    View Slide

  27. •Testable code
    •No hidden dependencies = Better
    API = Maintainability
    •All common initialization logic
    can be extracted (Dependency
    Injection Container)
    DI benefits
    Monday, September 26, 11

    View Slide

  28. •Long method call chains
    DI gotchas
    Monday, September 26, 11

    View Slide

  29. Law Of Demeter
    Method call chains are bad
    Monday, September 26, 11

    View Slide

  30. “Each unit should have only limited knowledge about other
    units: only units "closely" related to the current unit”
    “Each unit should only talk to its friends; don't talk to
    strangers”
    “Only talk to your immediate friends”
    wikipedia.org
    Monday, September 26, 11

    View Slide

  31. Bad
    Good
    Law Of Demeter
    //...
    $sku = $product->getStockItem()->getSku();
    //...
    $sku = $product->getItemSku();
    //Product.php
    class Product
    {
    //...
    public function getItemSku()
    {
    return $this->stockItem->getSku();
    }
    }
    Monday, September 26, 11

    View Slide

  32. Change-proof code
    Monday, September 26, 11

    View Slide

  33. Which one makes
    more sense?
    //...
    $logger->getWriter()->getFile()->writeLine('Some Log');
    //...
    $logger->log('Some Log');
    Monday, September 26, 11

    View Slide

  34. “Programmers should avoid writing code that looks like:
    dog.getBody().getTail().wag(); … The solution is … [to] …
    rewrite our example as: dog.expressHappiness(); and let
    the implementation of the dog decide what this means.”
    ThoughtWorks UK
    Monday, September 26, 11

    View Slide

  35. •Isolate ✔
    •Reproduce
    •Speed-up
    •Document
    TODO
    Monday, September 26, 11

    View Slide

  36. Control the interaction
    Property 2: Speed
    Property 3: Repeatability
    Monday, September 26, 11

    View Slide

  37. “… mock objects are simulated objects that mimic
    the behavior of real objects in controlled ways”
    wikipedia.org
    Monday, September 26, 11

    View Slide

  38. Test A
    Mock
    S
    S
    Mock objects
    Monday, September 26, 11

    View Slide

  39. •Test independence
    •Interface discovery
    Mock objects
    Monday, September 26, 11

    View Slide

  40. •By mocking out external
    dependencies, we can achieve
    faster and more granular tests,
    e.g. mock filesystem, database,
    third party api, etc.
    Test independence
    Monday, September 26, 11

    View Slide

  41. Isolating filesystem
    •Now we actually
    test that
    FileLogger
    correctly
    decorates the log
    string before
    writing it to
    file, leaving the
    actual write to
    file part to the
    File class and
    test.
    class FileLoggerTest extends PHPUnit_Framework_TestCase
    {
    //...
    public function testShouldFormatLogString()
    {
    $file = $this->getMock('File');
    $file
    ->expects($this->once())
    ->method('write')
    ->with(
    sprintf(
    '[ERR] %s some error',
    date(DATE_ISO8601)
    )
    )
    ;
    $logger = new FileLogger($file);
    $logger->err('some error');
    }
    }
    Monday, September 26, 11

    View Slide

  42. Methods, not functions
    $fp = fopen('/tmp/file.ext', 'w+');
    fwrite($fp, 'content');
    $file = new File('/tmp/file.ext', 'w+');
    $file->write('content');
    Monday, September 26, 11

    View Slide

  43. Mocking functions
    •An example of
    transforming a
    function into
    a method
    class File
    {
    private $handle;
    public function __construct($path, $mode)
    {
    $this->handle = fopen($path, $mode);
    }
    public function write($content, $length = null)
    {
    fwrite($this->handle, $content, $length);
    }
    public function __destruct()
    {
    fclose($this->handle);
    }
    }
    Monday, September 26, 11

    View Slide

  44. •Right now you know the least
    about your problem domain
    •Your knowledge of the domain
    grows as you spend more time
    working in it
    •Why write something that you'll
    throw away if you can mock it
    and see if it makes sense?
    Interface discovery
    Monday, September 26, 11

    View Slide

  45. Interface discovery
    •Had the example
    been written
    before the
    BankAccount class,
    it would’ve given
    us better
    understanding of
    the problem and
    could’ve helped
    design the
    BankAccount class
    itself
    class PaymentGatewayTest extends PHPUnit_Framework_TestCase
    {
    //...
    public function testShouldCreditAccountByAmountCharged()
    {
    $bankAccount = $this->getMock('BankAccount');
    $bankAccount
    ->expects($this->once())
    ->method('credit')
    ->with(100)
    ;
    $this->gateway->charge(100, $bankAccount);
    }
    }
    Monday, September 26, 11

    View Slide

  46. Interface discovery
    Test B
    S
    Mock
    T
    U
    T
    Mock
    U
    Monday, September 26, 11

    View Slide

  47. •So, it turns out, that the best
    use for mock objects is for
    iterative interface discovery
    Interface discovery
    Monday, September 26, 11

    View Slide

  48. Wait, what?
    Test driven
    development
    Monday, September 26, 11

    View Slide

  49. •Write a failing test case, use
    mock objects to represent
    classes that are not
    implemented
    Red
    Monday, September 26, 11

    View Slide

  50. •When the test makes sense and
    the intent is clear, write
    method bodies
    Green
    Monday, September 26, 11

    View Slide

  51. •Now that your tests are green,
    see what needs to be extracted
    (create dependencies, remove
    duplication)
    Refactor
    Monday, September 26, 11

    View Slide

  52. TDD example
    Monday, September 26, 11

    View Slide

  53. // file tests/PaymentGatewayTest.php
    require_once __DIR__.'/../lib/PaymentGateway.php';
    require_once __DIR__.'/../lib/BankAccount.php';
    class PaymentGatewayTest extends PHPUnit_Framework_TestCase
    {
    protected $gateway;
    public function setUp()
    {
    $this->gateway = new PaymentGateway();
    }
    public function tearDown()
    {
    unset($this->gateway);
    }
    public function testShouldCreditAccountByAmountCharged()
    {
    $bankAccount = $this->getMock('BankAccount');
    $bankAccount
    ->expects($this->once())
    ->method('credit')
    ->with(100)
    ;
    $this->gateway->charge(100, $bankAccount);
    }
    }
    Red
    Monday, September 26, 11

    View Slide

  54. // file lib/PaymentGateway.php
    class PaymentGateway
    {
    public function charge($amount, BankAccount $account)
    {
    }
    }
    // file lib/BankAccount.php
    class BankAccount
    {
    }
    Red
    Monday, September 26, 11

    View Slide

  55. $ phpunit tests/
    PHPUnit 3.5.0RC1 by Sebastian Bergmann.
    F
    Time: 0 seconds, Memory: 3.25Mb
    There was 1 failure:
    1) PaymentGatewayTest::testCharge
    Expectation failed for method name is equal to when
    invoked 1 time(s).
    Method was expected to be called 1 times, actually called 0 times.
    FAILURES!
    Tests: 1, Assertions: 1, Failures: 1.
    Red
    Monday, September 26, 11

    View Slide

  56. Green
    // file lib/PaymentGateway.php
    class PaymentGateway
    {
    public function charge($amount, BankAccount $account)
    {
    $account->credit($amount);
    }
    }
    // file lib/BankAccount.php
    class BankAccount
    {
    public function credit($amount)
    {
    }
    }
    Monday, September 26, 11

    View Slide

  57. $ phpunit tests/
    PHPUnit 3.5.0RC1 by Sebastian Bergmann.
    .
    Time: 0 seconds, Memory: 3.25Mb
    OK (1 test, 1 assertion)
    Green
    Monday, September 26, 11

    View Slide

  58. Mock only what you
    own...
    And what you don’t own, own
    Monday, September 26, 11

    View Slide

  59. Proxy
    •Classes change,
    method names,
    signature,
    visibility changes.
    •What will you do if
    a class changed one
    of its public
    methods to final?
    •Doctrine MongoDB
    ODM is built using
    proxies
    namespace My;
    class Mongo
    {
    private $mongo;
    public function __construct(\Mongo $mongo)
    {
    $this->mongo = $mongo;
    }
    public function selectDB($name)
    {
    return new MongoDB($this->mongo->selectDB($name));
    }
    }
    class MongoDB
    {
    private $mongoDb;
    public function __construct(\MongoDB $mongoDb)
    {
    $this->mongoDb = $mongoDb;
    }
    }
    Monday, September 26, 11

    View Slide

  60. Repeatability
    •If a class or library that you
    test relies on an external
    component which might not be
    present during testing and the
    absence of which might break
    the tests, make sure to add
    checks and mark tests as
    skipped
    Monday, September 26, 11

    View Slide

  61. Repeatability
    namespace Tests;
    class MongoTestCase extends \PHPUnit_Framework_TestCase
    {
    public function setUp()
    {
    if (!class_exists('Mongo')) {
    $this->markTestSkipped(
    'Mongo extension not installed'
    );
    }
    }
    }
    namespace Tests;
    class MongoTest extends MongoTestCase
    {
    public function setUp()
    {
    parent::setUp();
    //...
    }
    }
    Monday, September 26, 11

    View Slide

  62. The code evolution
    Refactoring
    Monday, September 26, 11

    View Slide

  63. “… refactoring is … changing … source code without modifying
    its external functional behavior in order to improve … the
    software.”
    wikipedia.org
    Monday, September 26, 11

    View Slide

  64. Refactoring
    Monday, September 26, 11

    View Slide

  65. •Best test code coverage
    possible
    •Design is born out of
    necessity, not forecast
    TDD benefits
    Monday, September 26, 11

    View Slide

  66. •Shared fixtures
    •External dependencies
    •Test code duplication
    •Over-mocking
    TDD gotchas
    Monday, September 26, 11

    View Slide

  67. Premature
    optimization
    Take care of speed after the design is
    done
    Monday, September 26, 11

    View Slide

  68. “Premature optimization is the root of all evil”
    C. A. R. Hoare, Computer Scientist
    “"Premature optimization" is a phrase used to describe a
    situation where a programmer lets performance
    considerations affect the design of a piece of code.”
    wikipedia.org
    Monday, September 26, 11

    View Slide

  69. class Product
    {
    protected $reviews;
    protected $reviewCount = 0;
    public function __construct(ArrayCollection $r = null)
    {
    $this->reviews = $r ?: new ArrayCollection();
    }
    public function getReviewCount()
    {
    return $this->reviewCount;
    }
    public function incrementReviewCount()
    {
    $this->reviewCount++;
    }
    public function decrementReviewCount()
    {
    $this->reviewCount--;
    }
    public function addReview(Review $review)
    {
    $this->reviews->add($review);
    $this->incrementReviewCount();
    }
    public function removeReview(Review $review)
    {
    $this->reviews->remove($review);
    $this->decrementReviewCount();
    }
    }
    Monday, September 26, 11

    View Slide

  70. class Product
    {
    private $reviews;
    public function __construct(ArrayCollection $r = null)
    {
    $this->reviews = $r ?: new ArrayCollection();
    }
    public function getReviewCount()
    {
    return $this->reviews->count();
    }
    public function addReview(Review $review)
    {
    $this->reviews->add($review);
    }
    public function removeReview(Review $review)
    {
    $this->reviews->remove($review);
    }
    }
    Monday, September 26, 11

    View Slide

  71. •Isolate ✔
    •Reproduce ✔
    •Speed-up ✔
    •Document
    TODO
    Monday, September 26, 11

    View Slide

  72. Keep your code clean
    Self-documentation
    Monday, September 26, 11

    View Slide

  73. •Don’t Repeat Yourself (DRY) -
    if a piece of code appears more
    than two times, extract it into
    a dedicated function, method or
    class
    •You ain’t gonna need it (YAGNI)
    - there is no need to implement
    ACL if you were asked to write
    a forum board, there might
    never be a need
    Monday, September 26, 11

    View Slide

  74. •Don’t optimize the code before
    it’s been written in the most
    straightforward way possible,
    forecasts are usually wrong
    anyway
    Monday, September 26, 11

    View Slide

  75. •Avoid magic, its easier to code
    when the API is not lying,
    dependency injection helps to
    get rid of magic
    •Avoid boolean parameters, they
    kill readability
    Monday, September 26, 11

    View Slide

  76. $service->processOrder(int $orderNumber, boolean $emailConfirmation = true);
    $service->processOrder(int $orderNumber);
    $service->emailConfirmation(int $orderNumber);
    Removing booleans
    Monday, September 26, 11

    View Slide

  77. •Use inheritance and
    polymorphism over conditionals
    •Use composition over
    inheritance
    Monday, September 26, 11

    View Slide

  78. The less the better
    Conditionals
    Monday, September 26, 11

    View Slide

  79. •Its probably not
    bad to have one if
    statement
    Conditional
    class MongoCursor
    {
    public function sort(array $fields)
    {
    if ($this->loggerCallable) {
    $this->log(array(
    'sort' => true,
    'fields' => $fields,
    ));
    }
    $this->mongoCursor->sort($fields);
    return $this;
    }
    }
    Monday, September 26, 11

    View Slide

  80. class MongoCollection
    {
    public function batchInsert(array &$a, array $options = array())
    {
    //...
    if ($this->loggerCallable) {
    $this->log(array(
    'batchInsert' => true,
    'num' => count($a),
    'data' => $a
    ));
    }
    $result = $this->mongoCollection->batchInsert($a, $options);
    return $result;
    }
    public function getDBRef(array $reference)
    {
    //...
    if ($this->loggerCallable) {
    $this->log(array(
    'get' => true,
    'reference' => $reference,
    ));
    }
    return $dbRef;
    }
    }
    Monday, September 26, 11

    View Slide

  81. •Every new method that needs to
    be logged, will have to add the
    if statement
    •There are plenty of if
    statements in this class
    Duplication
    Monday, September 26, 11

    View Slide

  82. •The class that has conditionals
    is much harder to understand
    and test, as there are several
    possible execution flows.
    •To test the previous example,
    you would need to instantiate
    the class with and without
    $loggerCallable and test each
    method twice.
    Conditionals
    Monday, September 26, 11

    View Slide

  83. •LoggableMongoCollection is
    responsible for logging
    collection interaction.
    •The regular MongoCollection
    class doesn’t need to know
    about logging
    Refactoring
    Monday, September 26, 11

    View Slide

  84. class MongoCollection
    {
    public function batchInsert(array &$a, array $options = array())
    {
    $result = $this->mongoCollection->batchInsert($a, $options);
    return $result;
    }
    public function getDBRef(array $reference)
    {
    return $dbRef;
    }
    }
    class LoggableMongoCollection extends MongoCollection
    {
    public function batchInsert(array &$a, array $options = array())
    {
    $this->log(array(
    'batchInsert' => true,
    'num' => count($a),
    'data' => $a
    ));
    return parent::batchInsert($a, $options);
    }
    public function getDBRef(array $reference)
    {
    $this->log(array(
    'get' => true,
    'reference' => $reference,
    ));
    return parent::getDBRef($reference);
    }
    }
    Monday, September 26, 11

    View Slide

  85. •Did my if statements disappear?
    •No
    •The conditional was actually
    moved to the instantiation part
    of the application, it is not
    part of the domain logic anymore
    •Testing such dedicated classes
    is much simpler, since there is
    only one execution flow
    Monday, September 26, 11

    View Slide

  86. class MongoCollectionFactory
    {
    protected $loggerCallable;
    public function __construct(\Closure $loggerCallable = null)
    {
    $this->loggerCallable = $loggerCallable;
    }
    public function getMongoCollection()
    {
    return isset($this->loggerCallable)
    ? new LoggableMongoCollection($this->loggerCallable)
    : new MongoCollection();
    }
    }
    Monday, September 26, 11

    View Slide

  87. •Dependency Injection helps to
    separate the “initialization”
    logic from “domain” logic
    •This allows for greater
    testability as each concern is
    tested in a dedicated
    environment
    Monday, September 26, 11

    View Slide

  88. • Consider the following
    controller:
    • It lets you disable, enable or
    list certain object type.
    • It uses switch statements to
    do so
    • It introduces tons of
    duplication
    • Each method will have at least
    three test case in order to
    achieve the necessary coverage
    • Why reinvent the type system
    if OOP already lets us use
    types (classes)?
    Switches
    class DisablerController extends Controller
    {
    public function disableAction($type)
    {
    switch ($type)
    {
    case 'product':
    case 'seller':
    case 'supplier':
    }
    }
    public function enableAction($type)
    {
    switch ($type)
    {
    case 'product':
    case 'seller':
    case 'supplier':
    }
    }
    public function listAction($type)
    {
    switch ($type)
    {
    case 'product':
    case 'seller':
    case 'supplier':
    }
    }
    }
    Monday, September 26, 11

    View Slide

  89. •Extract common
    interface
    Switches
    interface DisablerControllerInterface
    {
    function disableAction();
    function enableAction();
    function listAction();
    }
    Monday, September 26, 11

    View Slide

  90. Switches
    •Create your interface
    implementations – one
    implementation per switch
    condition
    •Classes can be tested in
    isolation
    Monday, September 26, 11

    View Slide

  91. class ProductDisablerController
    extends Controller
    implements DisablerControllerInterface
    {
    public function disableAction()
    {
    }
    public function enableAction()
    {
    }
    public function listAction()
    {
    }
    }
    class SellerDisablerController
    extends Controller
    implements DisablerControllerInterface
    {
    public function disableAction()
    {
    }
    public function enableAction()
    {
    }
    public function listAction()
    {
    }
    }
    class SupplierDisablerController
    extends Controller
    implements DisablerControllerInterface
    {
    public function disableAction()
    {
    }
    public function enableAction()
    {
    }
    public function listAction()
    {
    }
    }
    Monday, September 26, 11

    View Slide

  92. Polymorphism
    •Lighter classes
    •Usage of the true type system
    •You could also use abstract
    classes and complicated
    inheritance trees instead of
    one interface and orphaned
    implementations
    Monday, September 26, 11

    View Slide

  93. Composition
    Extract and inject re-used components
    instead of inheriting functionality
    Monday, September 26, 11

    View Slide

  94. •There might be cases when to
    receive an already-implemented
    functionality and keep it DRY,
    you extend the class
    encapsulating it
    •For example: a MongoCursor, that
    extends LoggableMongoCollection
    for its ->log() method
    Composition
    Monday, September 26, 11

    View Slide

  95. class LoggableMongoCollection extends MongoCollection
    {
    //...
    public function log(array $log)
    {
    if ( ! $this->loggerCallable) {
    return;
    }
    $log['class'] = $this->class->name;
    $log['db'] = $this->class->db;
    $log['collection'] = $this->class->collection;
    call_user_func($this->loggerCallable, $log);
    }
    }
    class MongoCursor extends LoggableMongoCollection
    implements \Iterator, \Countable
    {
    //...
    public function sort($fields)
    {
    if ($this->loggerCallable) {
    $this->log(array(
    'sort' => true,
    'fields' => $fields,
    ));
    }
    $this->mongoCursor->sort($fields);
    return $this;
    }
    }
    Monday, September 26, 11

    View Slide

  96. •But what happens if we need the
    same functionality in parallel
    hierarchies?
    •Single inheritance doesn't
    allow it, we end up duplicating
    the ->log() method in both
    hierarchies
    •Composition to the rescue
    Monday, September 26, 11

    View Slide

  97. Composition
    •The solution is to extract
    common functionality into a
    dedicated class
    Monday, September 26, 11

    View Slide

  98. class Logger
    {
    protected $loggerCallable;
    public function __construct(Closure $loggerCallable)
    {
    $this->loggerCallable = $loggerCallable;
    }
    public function log(array $log)
    {
    $log['class'] = $this->class->name;
    $log['db'] = $this->class->db;
    $log['collection'] = $this->class->collection;
    call_user_func($this->loggerCallable, $log);
    }
    }
    Monday, September 26, 11

    View Slide

  99. •Create an interface, that would
    define how the new component
    will compose the existing ones
    Composition
    Monday, September 26, 11

    View Slide

  100. interface LoggerContainer
    {
    //...
    public function setLogger(Logger $logger);
    }
    Monday, September 26, 11

    View Slide

  101. class MongoCursor implements Iterator, Countable
    {
    //...
    public function sort($fields)
    {
    $this->mongoCursor->sort($fields);
    return $this;
    }
    }
    class MongoCollection
    {
    //...
    public function batchInsert(array &$a, array $options = array())
    {
    //...
    $result = $this->mongoCollection->batchInsert($a, $options);
    return $result;
    }
    public function getDBRef(array $reference)
    {
    //...
    return $dbRef;
    }
    }
    Parent classes don’t log
    Monday, September 26, 11

    View Slide

  102. class LoggableMongoCursor extends MongoCursor
    implements LoggerContainer
    {
    //...
    public function setLogger(Logger $logger)
    {
    $this->logger = $logger;
    }
    //...
    public function sort($fields)
    {
    $this->logger->log(array(
    'sort' => true,
    'fields' => $fields,
    ));
    return parent::sort($fields);
    }
    }
    class LoggableMongoCollection extends MongoCollection
    implements LoggerContainer
    {
    //...
    public function setLogger(Logger $logger)
    {
    $this->logger = $logger;
    }
    public function batchInsert(array &$a, array $options = array())
    {
    $this->logger->log(array(
    'batchInsert' => true,
    'num' => count($a),
    'data' => $a
    ));
    return parent::batchInsert($a, $options);
    }
    public function getDBRef(array $reference)
    {
    $this->logger->log(array(
    'get' => true,
    'reference' => $reference,
    ));
    return parent::getDBRef($reference);
    }
    }
    Child classes that log
    Monday, September 26, 11

    View Slide

  103. •Don't decompose objects if
    there is no need for re-use,
    YAGNI
    Decomposition
    Monday, September 26, 11

    View Slide

  104. •Isolate ✔
    •Reproduce ✔
    •Speed-up ✔
    •Document ✔
    TODO
    Monday, September 26, 11

    View Slide

  105. Resources for self-improvement
    What's next?
    Monday, September 26, 11

    View Slide

  106. • Google Tech Talks - http://www.youtube.com/user/GoogleTechTalks
    • Inheritance, Polymorphism, & Testing
    • Unit Testing
    • Blogs
    • Invisible to the Eye – personal blog of Giorgio Sironi, Software
    Architect
    • Books
    • Design Patterns: Elements of Reusable Object-Oriented Software by Erich
    Gamma, Richard Helm, Ralph Johnson and John M. Vlissides, Addison-Wesley
    Professional, 1994
    • Refactoring: Improving the Design of Existing Code by Martin Fowler, Kent
    Beck, John Brant, William Opdyke and Don Roberts, Addison-Wesley
    Professional, 1999
    • Patterns of Enterprise Application Architecture by Martin Fowler,
    Addison-Wesley Professional, 2002
    • xUnit Test Patterns: Refactoring Test Code by Gerard Meszaros, Addison-
    Wesley, 2007
    • Growing Object-Oriented Software Guided by Tests by Steve Freeman and Nat
    Resources for self-
    improvement
    Monday, September 26, 11

    View Slide

  107. Now is the time to ask them
    Questions?
    Monday, September 26, 11

    View Slide