SlideShare a Scribd company logo
Mocking
 Demystified

               by @_md
how many of you
  write tests?
how many
write tests before the code?
how many of you
know what a mock is?
how many of you use mocks?
ALL YOU NEED TO KNOW ABOUT TESTING
           (in 5 minutes)
test

Arrange

          Act

                 Assert
test
        instantiate
Arrange
        tested object
       Act

              Assert
instantiate
          Arrange
                  tested object

$parser = new MarkdownParser;
test

Arrange
                  run the method
          Act     you want to test
                 Assert
run the method
          Act
                  you want to test

$parser = new MarkdownParser;

$html = $parser->toHtml('Hello World');
test

Arrange

          Act

                 Assert specify the
                        expected outcome
specify the
          Assert
                 expected outcome

$parser = new MarkdownParser;
$html = $parser->toHtml('Hello World');

assertTrue('<p>Hello World</p>' == $html);
test

Arrange         instantiate tested object

          Act        run the method

                Assert      check expected outcome
$this->toHtml('Hello World')
     ->shouldReturn('<p>Hello World</p>');
$this->toHtml('Hello World')
     ->shouldReturn('<p>Hello World</p>');




     a test is an executable example
      of a class expected behaviour
READY TO MOCK AROUND?
why?
class ParserSubject
{
    public function notify(Event $event)
    {
        foreach ($this->subscribers as $subscriber) {
            $subscriber->onChange($event);
        }
    }

}




                                 HOW DO I KNOW NOTIFY WORKS?
class ParserSubjectTest extends TestCase
{
    /** @test */
    function it_notifies_subscribers()
    {
         // arrange
        $parser = new ParserSubject;

        // act
        // I need an event!!!
        $parser->notify(/* $event ??? */);

        // assert
        // how do I know subscriber::onChange got called?
    }

}



                                 HOW DO I KNOW NOTIFY WORKS?
class EndOfListListener extends EventListener
{

    public function onNewLine(Event $event)
    {
        $html = $event->getText();
        if ($this->document->getNextLine() == "") {
            $html .= "</li></ul>";
        }
        return $html;
    }

}




                          HOW DO I CONTROL EVENT AND DOCUMENT
{
                  focus on unit
                  expensive to instantiate
      why?        control on collaborators’ state
                  indirect outputs
                  undesirable side effects
[Meszaros 2007]
a mock is just 1 of
many test double patterns
doubles are replacement of anything
    that is not the tested object
{
                       dummy
                       fake
             doubles   stub
                       mock
                       spy
[Meszaros 2007]
{
                  dummy no behaviour
                  fake
                         control indirect output
    doubles       stub
                  mock
                        check indirect output
                  spy
[Meszaros 2007]
a mockist TDD, London school, approach:


only the tested object is real
DOUBLES WITHOUT BEHAVIOUR
dummy




http://wicker123.deviantart.com/art/Slappy-The-Dummy-148136425
test

Arrange

          Act

                 Assert
arrange
          {
          instantiate (may require collaborators)
class MarkdownParser
{
    private $eventDispatcher;

    public function __construct(EventDispatcher $dispatcher)
    {
        $this->eventDispatcher = $dispatcher;
    }
}




                                USE DOUBLES TO BYPASS TYPE HINTING
class MarkdownParser
{
    private $eventDispatcher;

    public function __construct(EventDispatcher $dispatcher)
    {
        $this->eventDispatcher = $dispatcher;
    }
}




                                USE DOUBLES TO BYPASS TYPE HINTING
class MarkdownParserTest extends TestCase
{

    function setUp()
    {
        $dispatcher = $this->getMock('EventDispatcher');
        $this->parser = new MardownParser($dispatcher);
    }

}




                                               XUNIT EXAMPLE
class MarkdownParser extends ObjectBehavior
{

    function let($dispatcher)
    {
        $dispatcher->beAMockOf('EventDispatcher');
        $this->beConstructedWith($dispatcher);
    }

}




                                              PHPSPEC EXAMPLE
http://wicker123.deviantart.com/art/Slappy-The-Dummy-148136425
      “Dummy is a placeholder passed to the
       SUT (tested object), but never used”

[Meszaros 2007]
dummy: double with no behaviour




http://wicker123.deviantart.com/art/Slappy-The-Dummy-148136425
DOUBLES TO CONTROL INDIRECT OUPUT
arrange
          {
          instantiate (may require collaborators)
          further change state
class MarkdownParserTest extends TestCase
{

    function setUp()
    {
        $dispatcher = $this->getMock('EventDispatcher');
        $this->parser = new MardownParser($dispatcher);

        $this->parser->setEncoding('UTF-8');

    }

}




                         FURTHER CHANGE TO THE STATE IN ARRANGE
class MarkdownParserTest extends TestCase
{

    function setUp()
    {
        $dispatcher = $this->getMock('EventDispatcher');
        $this->parser = new MardownParser($dispatcher);

        $this->parser->setEncoding('UTF-8');

    }

}




                         FURTHER CHANGE TO THE STATE IN ARRANGE
arrange
          {
          instantiate (may require collaborators)
          further change state
          configure indirect output
class EndOfListListener extends EventListener
{

    public function onNewLine(Event $event)
    {
        $html = $event->getText();
        if ($this->document->getNextLine() == "") {
            $html .= "</li></ul>";
        }
        return $html;
    }

}




                                            INDIRECT OUTPUTS
class EndOfListListener extends EventListener
{

    public function onNewLine(Event $event)
    {
        $html = $event->getText();
        if ($this->document->getNextLine() == "") {
            $html .= "</li></ul>";
        }
        return $html;
    }

}




                                            INDIRECT OUTPUTS
fake
stub
                                        doubles for controlling indirect output




       http://www.flickr.com/photos/fcharlton/1841638596/
       http://www.flickr.com/photos/64749744@N00/476724045
$event->getText(); // will return “Hello World”
$this->document->getNextLine(); // will return “”




                 STUBBING: CONTROLLING DOUBLES INDIRECT OUTPUTS
function let($event, $document)
{
    $event->beAMockOf("Event");
    $document->beAMockOf("Document");
    $this->beConstructedWith($document);
}




                                           IN PHPSPEC
function let($event, $document)
{
    $event->beAMockOf("Event");
    $document->beAMockOf("Document");
    $this->beConstructedWith($document);
}

function it_ends_list_when_next_is_empty($event, $document)
{
    $event->getText()->willReturn("Some text");
    $document->getNextLine()->willReturn("");




}




                                                   IN PHPSPEC
function let($event, $document)
{
    $event->beAMockOf("Event");
    $document->beAMockOf("Document");
    $this->beConstructedWith($document);
}

function it_ends_list_when_next_is_empty($event, $document)
{
    $event->getText()->willReturn("Some text");
    $document->getNextLine()->willReturn("");

    $this->onNewLine($event)
         ->shouldReturn("Some text</li></ul>");

}




                                                   IN PHPSPEC
class EndOfListListener extends EventListener
{

    public function onNewLine(Event $event)
    {
        $html = $event->getText();
        if ($this->document->getNextLine() == "") {
            $html .= "</li></ul>";
        }
        return $html;
    }

}




                                                THE CODE AGAIN
function let($event, $document)
{
    $event->beAMockOf("Event");
    $document->beAMockOf("Document");
    $this->beConstructedWith($document);
}

function it_ends_list_when_next_is_empty($event, $document)
{
    $event->getText()->willReturn("Some text");
    $document->getNextLine()->willReturn("");

    $this->onNewLine($event)
         ->shouldReturn("Some text</li></ul>");

}




                                                  THE SPEC AGAIN
double                       behaviour

loose demand       returns null to any method call
fake           returns null to defined set methods
stub           returns value defined or raise error
DOUBLES TO SPEC MESSAGE EXCHANGE
we said before

a test is an executable example
 of a class expected behaviour
what is behaviour?
B = I + O


 behaviour = input + output
what can happen in a method?
{
          return a value
          modify state
methods   print something
          throw an exception
          delegate
{
          return a value
                          not the final
          modify state
                            behaviour
methods   print something
          throw an exception
          delegate
{
methods
          return a value we should probably
          print something delegate that too
          throw an exception
          delegate
{
methods
          return a value
          throw an exception
          delegate
method                strategy

returns a value
throws an exception

delegates
method                     strategy

returns a value         assertions/
throws an exception    expectations

delegates             mocks & spies
Viewpoints Research Institute Source - Bonnie Macbird URL -http://www.vpri.org
“The key in making great and growable systems is much more to
         design how its modules communicate
          rather than what their internal properties
                  and behaviours should be.”

                        Messaging
messaging
yeah, but what does it mean?
$this->person->getCar()->getEngine()->ignite();
Inappropriate Intimacy
$this->person->startCar();
tell, don’t ask!


[Sharp 1997]
exposing lower level implementation
       makes the code rigid
$this->person->getCar()->getEngine()->ignite();
focus on messaging
makes the code flexible
$this->person->startCar();
mock
spy
                                              doubles for messaging




               http://www.flickr.com/photos/scribe215/3234974208/
       http://www.flickr.com/photos/21560098@N06/5772919201/
class ParserSubject
{
    public function notify(Event $event)
    {
        foreach ($this->subscribers as $subscriber) {
            $subscriber->onChange($event);
        }
    }

}




                                 HOW DO I KNOW NOTIFY WORKS?
class ParserSubject
{
    public function notify(Event $event)
    {
        foreach ($this->subscribers as $subscriber) {
            $subscriber->onChange($event);
        }
    }

}




                                 HOW DO I KNOW NOTIFY WORKS?
use mocks to describe how your
method will impact collaborators
use PHPUnit_Framework_TestCase as TestCase;

class ParserSubjectTest extends TestCase
{
    /** @test */ function it_notifies_subscribers()
    {
        $event = $this->getMock("Event");

        $subscriber = $this->getMock("Subscriber");
        $subscriber->expects($this->once())
                   ->method("onChange")
                   ->with($event);

        $parser = new ParserSubject();
        $parser->attach($subscriber);
        $parser->notify($event);
    }
}


                           USING PHPUNIT NATIVE MOCK CONSTRUCT
public function testUpdateWithEqualTypes()
{
    $installer = $this->createInstallerMock();
    $manager   = new InstallationManager('vendor');
    $manager->addInstaller($installer);

    $initial   = $this->createPackageMock();
    $target    = $this->createPackageMock();
    $operation = new UpdateOperation($initial, $target, 'test');

    $initial
        ->expects($this->once())
        ->method('getType')
        ->will($this->returnValue('library'));
    $target
        ->expects($this->once())
        ->method('getType')
        ->will($this->returnValue('library'));

    $installer
        ->expects($this->once())
        ->method('supports')
        ->with('library')
        ->will($this->returnValue(true));

    $installer
        ->expects($this->once())
        ->method('update')
        ->with($this->repository, $initial, $target);




                                                                   EXCESSIVE SETUP
github.com/padraicb/mockery
use PHPUnit_Framework_TestCase as TestCase;

class ParserSubjectTest extends TestCase
{
    /** @test */ function it_notifies_subscribers()
    {
        $event = Mockery::mock("Event");

        $subscriber = Mockery::mock("Subscriber");
        $subscriber->shouldReceive("onChange")
                   ->with($event);

        $parser = new ParserSubject();
        $parser->attach($subscriber);
        $parser->notify($event);
    }
}



                                               USING MOCKERY
class ParserSubject extends PHPSpec2ObjectBehavior
{
    /**
      * @param Event $event
      * @param Subscriber $subscriber
      */
    function it_notifies_subscribers($event, $subscriber)
    {
         $subscriber->onChange($event)->shouldBeCalled();
         $this->attach($subscriber);

        $this->notify($event);
    }
}




                                      SAME EXAMPLE IN PHPSPEC
use spies to describe indirect output
   expectations, retrospectively
github.com/dancras/doubles
use PHPSpec2ObjectBehavior;

class ParserSubject extends ObjectBehavior
{
    function it_notifies_subscribers()
    {
        $event = Doubles::fromClass('Event');
        $subscriber = Doubles::fromClass('Subscriber');
        $this->attach($subscriber);
        $this->notify($event);

        $subscriber->spy('onChange')->callCount()->shoudBe(1);

    }
}




                        SPY EXAMPLE – WITH “DOUBLES” FRAMEWORK
Mocking Demystified
use PHPSpec2ObjectBehavior;

class ParserSubject extends ObjectBehavior
{
    /**
      * @param Event $event
      * @param Subscriber $subscriber
      */
    function it_notifies_subscribers($event, $subscriber)
    {
         $this->attach($subscriber);
         $this->notify($event);
         $subscriber->onChange($event)
                    ->shouldHaveBeenCalled();
    }
}




              SPY EXAMPLE – AVAILABLE WITH PROPHECY INTEGRATION
use ProphecyProphet;

class TestCase extends SomeUnitTestingFrameworkTestCase
{
    function createDoubler($name)
    {
        $this->prophet = new ProphecyProphet;
        return $this->prophet->prophesize($name);
    }

    function createDummy($name)
    {
        return $this->createDoubler($name)->reveal();
    }
}




                                         INTEGRATING PROPHECY
{
                  dummy no behaviour
                  fake
                        control indirect output
    doubles       stub
                  mock
                        messaging
                  spy
[Meszaros 2007]
Marcello Duarte

  I work here
I contribute here
  I tweet here @_md
Thank you !
Questions or Comments?




want to improve on testing? bit.ly/inviqa-bdd-training

More Related Content

Mocking Demystified

  • 2. how many of you write tests?
  • 3. how many write tests before the code?
  • 4. how many of you know what a mock is?
  • 5. how many of you use mocks?
  • 6. ALL YOU NEED TO KNOW ABOUT TESTING (in 5 minutes)
  • 7. test Arrange Act Assert
  • 8. test instantiate Arrange tested object Act Assert
  • 9. instantiate Arrange tested object $parser = new MarkdownParser;
  • 10. test Arrange run the method Act you want to test Assert
  • 11. run the method Act you want to test $parser = new MarkdownParser; $html = $parser->toHtml('Hello World');
  • 12. test Arrange Act Assert specify the expected outcome
  • 13. specify the Assert expected outcome $parser = new MarkdownParser; $html = $parser->toHtml('Hello World'); assertTrue('<p>Hello World</p>' == $html);
  • 14. test Arrange instantiate tested object Act run the method Assert check expected outcome
  • 15. $this->toHtml('Hello World') ->shouldReturn('<p>Hello World</p>');
  • 16. $this->toHtml('Hello World') ->shouldReturn('<p>Hello World</p>'); a test is an executable example of a class expected behaviour
  • 17. READY TO MOCK AROUND?
  • 18. why?
  • 19. class ParserSubject { public function notify(Event $event) { foreach ($this->subscribers as $subscriber) { $subscriber->onChange($event); } } } HOW DO I KNOW NOTIFY WORKS?
  • 20. class ParserSubjectTest extends TestCase { /** @test */ function it_notifies_subscribers() { // arrange $parser = new ParserSubject; // act // I need an event!!! $parser->notify(/* $event ??? */); // assert // how do I know subscriber::onChange got called? } } HOW DO I KNOW NOTIFY WORKS?
  • 21. class EndOfListListener extends EventListener { public function onNewLine(Event $event) { $html = $event->getText(); if ($this->document->getNextLine() == "") { $html .= "</li></ul>"; } return $html; } } HOW DO I CONTROL EVENT AND DOCUMENT
  • 22. { focus on unit expensive to instantiate why? control on collaborators’ state indirect outputs undesirable side effects [Meszaros 2007]
  • 23. a mock is just 1 of many test double patterns
  • 24. doubles are replacement of anything that is not the tested object
  • 25. { dummy fake doubles stub mock spy [Meszaros 2007]
  • 26. { dummy no behaviour fake control indirect output doubles stub mock check indirect output spy [Meszaros 2007]
  • 27. a mockist TDD, London school, approach: only the tested object is real
  • 30. test Arrange Act Assert
  • 31. arrange { instantiate (may require collaborators)
  • 32. class MarkdownParser { private $eventDispatcher; public function __construct(EventDispatcher $dispatcher) { $this->eventDispatcher = $dispatcher; } } USE DOUBLES TO BYPASS TYPE HINTING
  • 33. class MarkdownParser { private $eventDispatcher; public function __construct(EventDispatcher $dispatcher) { $this->eventDispatcher = $dispatcher; } } USE DOUBLES TO BYPASS TYPE HINTING
  • 34. class MarkdownParserTest extends TestCase { function setUp() { $dispatcher = $this->getMock('EventDispatcher'); $this->parser = new MardownParser($dispatcher); } } XUNIT EXAMPLE
  • 35. class MarkdownParser extends ObjectBehavior { function let($dispatcher) { $dispatcher->beAMockOf('EventDispatcher'); $this->beConstructedWith($dispatcher); } } PHPSPEC EXAMPLE
  • 36. http://wicker123.deviantart.com/art/Slappy-The-Dummy-148136425 “Dummy is a placeholder passed to the SUT (tested object), but never used” [Meszaros 2007]
  • 37. dummy: double with no behaviour http://wicker123.deviantart.com/art/Slappy-The-Dummy-148136425
  • 38. DOUBLES TO CONTROL INDIRECT OUPUT
  • 39. arrange { instantiate (may require collaborators) further change state
  • 40. class MarkdownParserTest extends TestCase { function setUp() { $dispatcher = $this->getMock('EventDispatcher'); $this->parser = new MardownParser($dispatcher); $this->parser->setEncoding('UTF-8'); } } FURTHER CHANGE TO THE STATE IN ARRANGE
  • 41. class MarkdownParserTest extends TestCase { function setUp() { $dispatcher = $this->getMock('EventDispatcher'); $this->parser = new MardownParser($dispatcher); $this->parser->setEncoding('UTF-8'); } } FURTHER CHANGE TO THE STATE IN ARRANGE
  • 42. arrange { instantiate (may require collaborators) further change state configure indirect output
  • 43. class EndOfListListener extends EventListener { public function onNewLine(Event $event) { $html = $event->getText(); if ($this->document->getNextLine() == "") { $html .= "</li></ul>"; } return $html; } } INDIRECT OUTPUTS
  • 44. class EndOfListListener extends EventListener { public function onNewLine(Event $event) { $html = $event->getText(); if ($this->document->getNextLine() == "") { $html .= "</li></ul>"; } return $html; } } INDIRECT OUTPUTS
  • 45. fake stub doubles for controlling indirect output http://www.flickr.com/photos/fcharlton/1841638596/ http://www.flickr.com/photos/64749744@N00/476724045
  • 46. $event->getText(); // will return “Hello World” $this->document->getNextLine(); // will return “” STUBBING: CONTROLLING DOUBLES INDIRECT OUTPUTS
  • 47. function let($event, $document) { $event->beAMockOf("Event"); $document->beAMockOf("Document"); $this->beConstructedWith($document); } IN PHPSPEC
  • 48. function let($event, $document) { $event->beAMockOf("Event"); $document->beAMockOf("Document"); $this->beConstructedWith($document); } function it_ends_list_when_next_is_empty($event, $document) { $event->getText()->willReturn("Some text"); $document->getNextLine()->willReturn(""); } IN PHPSPEC
  • 49. function let($event, $document) { $event->beAMockOf("Event"); $document->beAMockOf("Document"); $this->beConstructedWith($document); } function it_ends_list_when_next_is_empty($event, $document) { $event->getText()->willReturn("Some text"); $document->getNextLine()->willReturn(""); $this->onNewLine($event) ->shouldReturn("Some text</li></ul>"); } IN PHPSPEC
  • 50. class EndOfListListener extends EventListener { public function onNewLine(Event $event) { $html = $event->getText(); if ($this->document->getNextLine() == "") { $html .= "</li></ul>"; } return $html; } } THE CODE AGAIN
  • 51. function let($event, $document) { $event->beAMockOf("Event"); $document->beAMockOf("Document"); $this->beConstructedWith($document); } function it_ends_list_when_next_is_empty($event, $document) { $event->getText()->willReturn("Some text"); $document->getNextLine()->willReturn(""); $this->onNewLine($event) ->shouldReturn("Some text</li></ul>"); } THE SPEC AGAIN
  • 52. double behaviour loose demand returns null to any method call fake returns null to defined set methods stub returns value defined or raise error
  • 53. DOUBLES TO SPEC MESSAGE EXCHANGE
  • 54. we said before a test is an executable example of a class expected behaviour
  • 56. B = I + O behaviour = input + output
  • 57. what can happen in a method?
  • 58. { return a value modify state methods print something throw an exception delegate
  • 59. { return a value not the final modify state behaviour methods print something throw an exception delegate
  • 60. { methods return a value we should probably print something delegate that too throw an exception delegate
  • 61. { methods return a value throw an exception delegate
  • 62. method strategy returns a value throws an exception delegates
  • 63. method strategy returns a value assertions/ throws an exception expectations delegates mocks & spies
  • 64. Viewpoints Research Institute Source - Bonnie Macbird URL -http://www.vpri.org “The key in making great and growable systems is much more to design how its modules communicate rather than what their internal properties and behaviours should be.” Messaging
  • 66. yeah, but what does it mean?
  • 71. exposing lower level implementation makes the code rigid
  • 73. focus on messaging makes the code flexible
  • 75. mock spy doubles for messaging http://www.flickr.com/photos/scribe215/3234974208/ http://www.flickr.com/photos/21560098@N06/5772919201/
  • 76. class ParserSubject { public function notify(Event $event) { foreach ($this->subscribers as $subscriber) { $subscriber->onChange($event); } } } HOW DO I KNOW NOTIFY WORKS?
  • 77. class ParserSubject { public function notify(Event $event) { foreach ($this->subscribers as $subscriber) { $subscriber->onChange($event); } } } HOW DO I KNOW NOTIFY WORKS?
  • 78. use mocks to describe how your method will impact collaborators
  • 79. use PHPUnit_Framework_TestCase as TestCase; class ParserSubjectTest extends TestCase { /** @test */ function it_notifies_subscribers() { $event = $this->getMock("Event"); $subscriber = $this->getMock("Subscriber"); $subscriber->expects($this->once()) ->method("onChange") ->with($event); $parser = new ParserSubject(); $parser->attach($subscriber); $parser->notify($event); } } USING PHPUNIT NATIVE MOCK CONSTRUCT
  • 80. public function testUpdateWithEqualTypes() { $installer = $this->createInstallerMock(); $manager = new InstallationManager('vendor'); $manager->addInstaller($installer); $initial = $this->createPackageMock(); $target = $this->createPackageMock(); $operation = new UpdateOperation($initial, $target, 'test'); $initial ->expects($this->once()) ->method('getType') ->will($this->returnValue('library')); $target ->expects($this->once()) ->method('getType') ->will($this->returnValue('library')); $installer ->expects($this->once()) ->method('supports') ->with('library') ->will($this->returnValue(true)); $installer ->expects($this->once()) ->method('update') ->with($this->repository, $initial, $target); EXCESSIVE SETUP
  • 82. use PHPUnit_Framework_TestCase as TestCase; class ParserSubjectTest extends TestCase { /** @test */ function it_notifies_subscribers() { $event = Mockery::mock("Event"); $subscriber = Mockery::mock("Subscriber"); $subscriber->shouldReceive("onChange") ->with($event); $parser = new ParserSubject(); $parser->attach($subscriber); $parser->notify($event); } } USING MOCKERY
  • 83. class ParserSubject extends PHPSpec2ObjectBehavior { /** * @param Event $event * @param Subscriber $subscriber */ function it_notifies_subscribers($event, $subscriber) { $subscriber->onChange($event)->shouldBeCalled(); $this->attach($subscriber); $this->notify($event); } } SAME EXAMPLE IN PHPSPEC
  • 84. use spies to describe indirect output expectations, retrospectively
  • 86. use PHPSpec2ObjectBehavior; class ParserSubject extends ObjectBehavior { function it_notifies_subscribers() { $event = Doubles::fromClass('Event'); $subscriber = Doubles::fromClass('Subscriber'); $this->attach($subscriber); $this->notify($event); $subscriber->spy('onChange')->callCount()->shoudBe(1); } } SPY EXAMPLE – WITH “DOUBLES” FRAMEWORK
  • 88. use PHPSpec2ObjectBehavior; class ParserSubject extends ObjectBehavior { /** * @param Event $event * @param Subscriber $subscriber */ function it_notifies_subscribers($event, $subscriber) { $this->attach($subscriber); $this->notify($event); $subscriber->onChange($event) ->shouldHaveBeenCalled(); } } SPY EXAMPLE – AVAILABLE WITH PROPHECY INTEGRATION
  • 89. use ProphecyProphet; class TestCase extends SomeUnitTestingFrameworkTestCase { function createDoubler($name) { $this->prophet = new ProphecyProphet; return $this->prophet->prophesize($name); } function createDummy($name) { return $this->createDoubler($name)->reveal(); } } INTEGRATING PROPHECY
  • 90. { dummy no behaviour fake control indirect output doubles stub mock messaging spy [Meszaros 2007]
  • 91. Marcello Duarte I work here I contribute here I tweet here @_md
  • 93. Questions or Comments? want to improve on testing? bit.ly/inviqa-bdd-training