PHP Day Verona 2015 - PHP object mocking framework world

PHP Day Verona 2015 - PHP object mocking framework world

Heard about PHPSpec? Well its PHP object mocking framework called Prophecy is quite nice. We'll discover its API, similarities and improvements regarding the one from PHPUnit. Finally, we'll take a look at the integration of Prophecy in PHPUnit.

34ade09dd3d11004ca8ee4174fd3d6a2?s=128

Sarah KHALIL

May 15, 2015
Tweet

Transcript

  1. PHP OBJECT MOCKING FRAMEWORK WORLD LET'S COMPARE PROPHECY AND PHPUNIT

    Sarah Khalil - @saro0h
  2. WHO AM I? • Head of • Trainer & Developer

    • Enjoying sharer • Contributor to
  3. TODAY, HOPEFULLY, WE LEARN NEW THINGS 1. Terminology about objects

    doubling 2. PHPUnit implementation 3. Prophecy implementation 4. The differences between the two philosophies
  4. TERMINOLOGY

  5. Dummies are objects that are passed around but never used.

    Fulfills a contract.
  6. Stubs are objects that implement the same methods as the

    real objects. Returns a specific value.
  7. Mocks are pre-programmed with expectations which forming a specification of

    the calls they are expected to receive. They can throw an exception if they receive a call they don't expect and are checked during verification to ensure they got all the calls they were expecting. Has expectation.
  8. PHPUNIT MOCKING LIBRARY

  9. INSTALL IT

  10. FUNDAMENTALS

  11. Your test class must extend PHPUnit_Framework_TestCase

  12. The method you need to know $this->getMock()

  13. • Generates an object. • All methods return NULL. •

    You can describe the expected behavior of your object.
  14. DUMMY $dummy = $this->getMock('Namespace');

  15. STUB $event = $this->getMock( ‘Symfony\Component\HttpKernel\Event\GetResponseEvent’ ); $event ->method('getRequest') ->will($this->returnValue($this->request)) ;

  16. MOCK $dispatcher = $this->getMock('Symfony\Component \EventDispatcher\EventDispatcherInterface'); $dispatcher ->expects($this->once()) ->method(‘dispatch') ->with( $this->equalTo(SecurityEvents::INTERACTIVE_LOGIN),

    $this->equalTo($loginEvent) ) ;
  17. REAL LIFE EXAMPLE

  18. None
  19. None
  20. Who knows exactly how the Oauth dance of Github works?

  21. IMPLEMENTATION OF THE FEATURE

  22. namespace PoleDev\AppBundle\Security; use Guzzle\Service\Client; use Symfony\[…]\Router; use Symfony\[…]\Response; use Symfony\[…]\Request;

    use Symfony\[…]\SimplePreAuthenticatorInterface; use Symfony\[…]\AuthenticationFailureHandlerInterface; use Symfony\[…]\TokenInterface; use Symfony\[…]\UserProviderInterface; use Symfony\[…]\AuthenticationException; use Symfony\[…]\UrlGeneratorInterface; use Symfony\[…]\HttpException; use Symfony\[…]\PreAuthenticatedToken; class GithubAuthenticator implements SimplePreAuthenticatorInterface, AuthenticationFailureHandlerInterface { // Some code… }
  23. private $client; private $router; public function __construct( Client $client, Router

    $router, $clientId, $clientSecret ) { $this->client = $client; $this->router = $router; $this->clientId = $clientId; $this->clientSecret = $clientSecret; }
  24. function createToken(Request $request, $providerKey) { $request = $this->client->post(…); $response =

    $request->send(); $data = $response->json(); if (isset($data[‘error']) && !is_null($this->logger) { $message = ‘An error occured…’; $this->logger->notice($message); throw new HttpException(401, $message); } return new PreAuthenticatedToken( ‘anon.’, $data[‘access_token’], $providerKey ); }
  25. ZOOM IN

  26. PART 1/3: GET ACCESS TOKEN $url = $this->router->generate(‘admin’,[], true); $endpoint

    = ‘/login/oauth/access_token’; $guzzleRequest = $this->client->post($endpoint,[], [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'code' => $request->get('code'), 'redirect_uri' => $url ]); $response = $guzzleRequest->send(); $data = $response->json(); createToken()
  27. PART 2/3: IF ERROR FROM GITHUB, EXCEPTION if (isset($data['error'])) {

    $message = 'An error occured during authentication with Github.'; $this->logger->notice($message, [ 'HTTP_CODE_STATUS' => 401, ‘error' => $data['error'], 'error_description' => $data['error_description'], ]); throw new HttpException(401, $message); } createToken()
  28. PART 3/3: CREATE AUTHENTICATED TOKEN return new PreAuthenticatedToken( 'anon.', $data[‘access_token’],

    $providerKey ); createToken()
  29. None
  30. None
  31. public function createToken(Request $request, $providerKey) { $request = $this->client->post('/login/oauth/access_token', array(),

    array ( 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'code' => $request->get('code'), 'redirect_uri' => $this->router->generate('admin', array(), UrlGeneratorInterface::ABSOLUTE_URL) )); $response = $request->send(); $data = $response->json(); if (isset($data['error'])) { $message = sprintf('An error occured during authentication with Github. (%s)', $data['error_description']); $this->logger->notice( $message, array( 'HTTP_CODE_STATUS' => Response::HTTP_UNAUTHORIZED, 'error' => $data['error'], 'error_description' => $data['error_description'], ) ); throw new HttpException(Response::HTTP_UNAUTHORIZED, $message); } return new PreAuthenticatedToken( ‘anon.', $data[‘access_token'], $providerKey ); } We need to test the result of createToken method
  32. namespace PoleDev\AppBundle\Tests\Security; class GithubAuthenticatorTest extends \PHPUnit_Framework_TestCase { public function testCreateToken()

    { } } CREATE THE TEST CLASS
  33. LET’S GET OUR DUMMIES AND CALL OUR METHOD TO TEST

    public function testCreateToken() { $githubAuthenticator = new GithubAuthenticator( $client, $router, '', '' ); $token = $githubAuthenticator ->createToken($request, ‘secure_area') ; }
  34. public function testCreateToken() { $githubAuthenticator = new GithubAuthenticator( $client, $router,

    '', '' ); $token = $githubAuthenticator ->createToken($request, ‘secure_area') ; } Where do they come from?
  35. To construct the GithubAuthenticator, we need: • Guzzle\Service\Client • Symfony\Bundle\FrameworkBundle\Routing\Router

    • $clientId = ‘’ • $clientSecret = ‘’ Step 1 fulfill the contract
  36. To call the createToken() method, we need: • Symfony\Component\HttpFoundation\Request •

    $providerKey = 'secure_area' Step 1 fulfill the contract
  37. $client = $this->getMock(‘Guzzle\Service \Client’); $router = $this->getMock('Symfony\Bundle \FrameworkBundle\Routing\Router'); $request =

    $this->getMock('Symfony\Component \HttpFoundation\Request'); This a dummy Step 1 fulfill the contract
  38. RUN THE TEST SUITE

  39. Is it really all about contracts? Step 1 fulfill the

    contract
  40. Step 1 fulfill the contract

  41. We must disable the use of the original Router constructor

    as we don’t actually care. Step 2 get the router
  42. $router = $this->getMockBuilder('Symfony \Bundle\FrameworkBundle\Routing\Router') ->disableOriginalConstructor() ->getMock() ; Step 2 get

    the router
  43. $ phpunit -c app/ src/PoleDev/AppBundle/Tests/ Security/GithubAuthenticatorTest.php PHP Fatal error: Call

    to a member function send() on null in /[…]/ AppBundle/Security/ GithubAuthenticator.php on line 43 Step 2 get the router
  44. 4 OBJECTS TO FAKE public function createToken(Request $request, $providerKey) {

    // … $url = $this->router->generate(‘admin’,[], true); $endpoint = ‘/login/oauth/access_token’; $guzzleRequest = $this->client->post($endpoint,[], [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'code' => $request->get('code'), 'redirect_uri' => $url ]); $response = $guzzleRequest->send(); $data = $response->json(); } 1 3 2 4 Step 3 fake data from Github
  45. public function createToken(Request $request, $providerKey) { // … $url =

    $this->router->generate(‘admin’,[], true); $endpoint = ‘/login/oauth/access_token’; $guzzleRequest = $this->client->post($endpoint,[], [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'code' => $request->get('code'), 'redirect_uri' => $url ]); $response = $guzzleRequest->send(); $data = $response->json(); } 1 Step 3 fake data from Github
  46. We need to stub the call to the method $router->generate()

    it needs to return an url 1 Step 3 fake data from Github
  47. $router ->method('generate') ->with('admin',[], true) ->willReturn('http://domain.name') ; 1 STUB THE ROUTER::GENERATE

    Step 3 fake data from Github
  48. public function createToken(Request $request, $providerKey) { // … $url =

    $this->router->generate(‘admin’,[], true); $endpoint = ‘/login/oauth/access_token’; $guzzleRequest = $this->client->post($endpoint,[], [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'code' => $request->get('code'), 'redirect_uri' => $url ]); $response = $request->send(); $data = $response->json(); } 2 Step 3 fake data from Github
  49. We need to create a dummy Guzzle\Http\Message \EntityEnclosingRequest $guzzleRequest 2

    Step 3 fake data from Github
  50. $guzzleRequest = $this ->getMockBuilder('Guzzle\Http \Message\EntityEnclosingRequest') ->disableOriginalConstructor() ->getMock() ; 2 GET

    THE GUZZLE REQUEST DUMMY Step 3 fake data from Github
  51. public function createToken(Request $request, $providerKey) { // … $url =

    $this->router->generate(‘admin’,[], true); $endpoint = ‘/login/oauth/access_token’; $guzzleRequest = $this->client->post($endpoint,[], [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'code' => $request->get('code'), 'redirect_uri' => $url ]); $response = $request->send(); $data = $response->json(); } 3 Step 3 fake data from Github
  52. We need to stub the call to the method $client->post(),

    it needs to return a $guzzleRequest 3 Step 3 fake data from Github
  53. $endpoint = '/login/oauth/access_token'; $client ->method('post') ->with($endpoint, [], [ 'client_id' =>

    '', 'client_secret' => '', 'code' => '', 'redirect_uri' => 'http://domain.name' ]) ->willReturn($guzzleRequest) ; 3 STUB THE GUZZLECLIENT::POST Step 3 fake data from Github
  54. public function createToken(Request $request, $providerKey) { // … $url =

    $this->router->generate(‘admin’,[], true); $endpoint = ‘/login/oauth/access_token’; $guzzleRequest = $this->client->post($endpoint,[], [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'code' => $request->get('code'), 'redirect_uri' => $url ]); $response = $guzzleRequest->send(); $data = $response->json(); } 4 Step 3 fake data from Github
  55. This method must return a: Guzzle\Http\Message\Response $response Let’s go for

    it! Step 3 fake data from Github STUB THE GUZZLEREQUEST::SEND 4
  56. $guzzleResponse = $this->getMockBuilder('Guzzle \Http\Message\Response') ->disableOriginalConstructor() ->getMock() ; $guzzleRequest ->method(‘send') ->willReturn($guzzleResponse)

    ; 4 Step 3 fake data from Github STUB THE GUZZLEREQUEST::SEND
  57. Hurray! The original code is running with our dummies and

    stubs. But we do not test anything.
  58. None
  59. public function createToken(Request $request, $providerKey) { $request = $this->client->post('/login/oauth/access_token', array(),

    array ( 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'code' => $request->get('code'), 'redirect_uri' => $this->router->generate('admin', array(), UrlGeneratorInterface::ABSOLUTE_URL) )); $response = $request->send(); $data = $response->json(); if (isset($data['error'])) { $message = sprintf('An error occured during authentication with Github. (%s)', $data['error_description']); $this->logger->notice( $message, array( 'HTTP_CODE_STATUS' => Response::HTTP_UNAUTHORIZED, 'error' => $data['error'], 'error_description' => $data['error_description'], ) ); throw new HttpException(Response::HTTP_UNAUTHORIZED, $message); } return new PreAuthenticatedToken( ‘anon.', $data[‘access_token'], $providerKey ); } We need to test the result of createToken method
  60. $token = $githubAuthenticator->createToken($request, ‘secure_area’); $this->assertSame( ‘a_fake_access_token', $token->getCredentials() ); $this->assertSame( ‘secure_area',

    $token->getProviderKey() ); $this->assertSame( ‘anon.', $token->getUser() ); $this->assertEmpty($token->getRoles()); $this->assertFalse($token->isAuthenticated()); $this->assertEmpty($token->getAttributes()); Test that the TOKEN is what we need to be. Step 4 assertions
  61. Step 3 bis forgot a thing (sorry)

  62. None
  63. Duck! Our token has its credentials at null. You need

    to provide it as the real code would have done it. Github returns an access token at that point.
  64. $guzzleResponse ->method('json') ->willReturn([ ‘access_token' => ‘a_fake_access_token’ ]) ; Step 3

    bis forgot a thing (sorry)
  65. Hurray! The original code is running with our dummies and

    stubs. And it is tested !
  66. None
  67. ADD EXPECTATION: MOCK $guzzleResponse ->expects($this->once()) ->method('json') ->willReturn([ ‘access_token' => ‘a_fake_access_token'

    ]) ; Expectation creates a new assertion.
  68. PROPHECY

  69. None
  70. INSTALL IT ;)

  71. FUNDAMENTALS

  72. PROPHET $prophet = new \Prophecy\Prophet;

  73. DUMMY $routerObjectProphecy = $prophet ->prophesize( 'Symfony\Bundle\FrameworkBundle\Routing\Router' ) ; dummy $router

    = $routerObjectProphecy->reveal();
  74. PROPHECY Object used to describe the future of your objects.

    $prophecy = $prophet->prophesize(‘YourClass’);
  75. Note that the prophet won’t use the original constructor by

    default. beConstructedWith($object) beConstructedThrough(‘method’, [$argument]) in PhpSpec
  76. OBJECT DOUBLE The goal is to get the object double

    $prophecy->reveal();
  77. STUB 1/2 • Get a stub out from the Router

    of Symfony. • $router->generate() must return 'http://www.google.com'
  78. STUB 2/2 $prophecy ->generate(‘route_name’) ->willReturn(‘http://www.google.com’) ;

  79. PROMISE

  80. willReturn() is a promise.

  81. A promise is a piece of code allowing a method

    call with a certain argument (if there is one), returns always the same value.
  82. $prophecy->willReturn(‘my value’); Returns the value ‘my value’. $prophecy->willReturnArgument(); Returns the

    first method argument. $prophecy->willThrow(‘ExceptionClass’); Throws an exception. $prophecy->will($callback) $prophecy->will(new Prophecy\Promise \ReturnPromise(array(‘my value’)); === $prophecy->willReturn(‘my value’); PROMISE - API https://github.com/phpspec/prophecy/blob/master/src/Prophecy/Prophecy/MethodProphecy.php Details about the implementation:
  83. NOT ENOUGH? Implement the Prophecy\Promise\PromiseInterface

  84. ARGUMENT

  85. HARD CODED ARGUMENT $prophecy ->generate(‘route_name’) ->willReturn(‘http://www.google.com’) ;

  86. NO HARD CODE • Prophecy offers you plenty of methods

    to « wildcard » the arguments. • Any argument is ok for the method you are « stubbing ». Prophecy\Argument::any()
  87. $prophecy ->myMethod(Prophecy\Argument::any()) ;

  88. ARGUMENT THE API • Pretty complete • To go further

    with this: • https://github.com/phpspec/prophecy#arguments-wildcarding
  89. MOCK

  90. ADD EXPECTATIONS $prophecy ->generate(‘route_name’) ->willReturn(‘http://www.google.com’) ->shouldBeCalled() ;

  91. We expect to have that method generate() called at least

    one time. How we call it in real life ? Predictions!
  92. PREDICTIONS API shouldBeCalled() shouldNotBeCalled() shouldBeCalledTimes(2)

  93. NOT ENOUGH? Implement the Prediction\PredictionInterface

  94. $prophet->checkPredictions(); (in your tearDown() method to check for all of

    your tests)
  95. If the prediction fails, it throws an exception.

  96. SPIES

  97. Verifies that a method has been called during the execution

    $em = $prophet->prophesize('Doctrine\ORM\EntityManager'); $controller->createUser($em->reveal()); $em->flush()->shouldHaveBeenCalled(); Exemple taken from the official prophecy repository
  98. REAL LIFE EXAMPLE

  99. None
  100. None
  101. THE ORIGINAL CODE You don’t remember right…

  102. namespace PoleDev\AppBundle\Security; use Guzzle\Service\Client; use Symfony\[…]\Router; use Symfony\[…]\Response; use Symfony\[…]\Request;

    use Symfony\[…]\SimplePreAuthenticatorInterface; use Symfony\[…]\AuthenticationFailureHandlerInterface; use Symfony\[…]\TokenInterface; use Symfony\[…]\UserProviderInterface; use Symfony\[…]\AuthenticationException; use Symfony\[…]\UrlGeneratorInterface; use Symfony\[…]\HttpException; use Symfony\[…]\PreAuthenticatedToken; class GithubAuthenticator implements SimplePreAuthenticatorInterface, AuthenticationFailureHandlerInterface { // Some code… }
  103. private $client; private $router; public function __construct( Client $client, Router

    $router, $clientId, $clientSecret ) { $this->client = $client; $this->router = $router; $this->clientId = $clientId; $this->clientSecret = $clientSecret; }
  104. function createToken(Request $request, $providerKey) { $request = $this->client->post(…); $response =

    $request->send(); $data = $response->json(); if (isset($data[‘error']) && !is_null($this->logger) { $message = ‘An error occured…’; $this->logger->notice($message); throw new HttpException(401, $message); } return new PreAuthenticatedToken( ‘anon.’, $data[‘access_token’], $providerKey ); }
  105. PART 1/3: GET ACCESS TOKEN $url = $this->router->generate(‘admin’,[], true); $endpoint

    = ‘/login/oauth/access_token’; $guzzleRequest = $this->client->post($endpoint,[], [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'code' => $request->get('code'), 'redirect_uri' => $url ]); $response = $guzzleRequest->send(); $data = $response->json(); createToken()
  106. PART 3/3: CREATE AUTHENTICATED TOKEN return new PreAuthenticatedToken( 'anon.', $data[‘access_token’],

    $providerKey ); createToken()
  107. LET’S TEST IT! AGAIN !

  108. public function createToken(Request $request, $providerKey) { $request = $this->client->post('/login/oauth/access_token', array(),

    array ( 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'code' => $request->get('code'), 'redirect_uri' => $this->router->generate('admin', array(), UrlGeneratorInterface::ABSOLUTE_URL) )); $response = $request->send(); $data = $response->json(); if (isset($data['error'])) { $message = sprintf('An error occured during authentication with Github. (%s)', $data['error_description']); $this->logger->notice( $message, array( 'HTTP_CODE_STATUS' => Response::HTTP_UNAUTHORIZED, 'error' => $data['error'], 'error_description' => $data['error_description'], ) ); throw new HttpException(Response::HTTP_UNAUTHORIZED, $message); } return new PreAuthenticatedToken( ‘anon.', $data[‘access_token'], $providerKey ); } We need to test the result of createToken method
  109. namespace PoleDev\AppBundle\Tests\Security; class GithubAuthenticatorTest extends \PHPUnit_Framework_TestCase { public function testCreateToken()

    { } } CREATE THE TEST CLASS
  110. FIRST, GET THE PROPHET namespace PoleDev\AppBundle\Tests\Security; class GithubAuthenticatorTest extends \PHPUnit_Framework_TestCase

    { private $prophet; public function testCreateToken() { } public function setUp() { $this->prophet = new \Prophecy\Prophet; } public function tearDown() { $this->prophet = null; } }
  111. public function testCreateToken() { $githubAuthenticator = new GithubAuthenticator( $client, $router,

    '', '' ); $token = $githubAuthenticator ->createToken($request, ‘secure_area') ; } We need to create those dependencies.
  112. LET’S GET OUR DUMMIES AND CALL OUR METHOD TO TEST

    public function testCreateToken() { $clientObjectProphecy = $this->prophet->prophesize( ‘Guzzle\Service\Client’ ); $client = $clientObjectProphecy->reveal(); // … } This a prophecy This a dummy
  113. $routerObjectProphecy = $this->prophet ->prophesize('Symfony\Bundle \FrameworkBundle\Routing\Router'); $router = $routerObjectProphecy->reveal(); $requestObjProphecy =

    $this->prophet ->prophesize('Symfony\Component \HttpFoundation\Request'); $request = $requestObjProphecy->reveal(); Step 1 fulfill the contract
  114. $ phpunit -c app/ src/PoleDev/AppBundle/Tests/ Security/GithubAuthenticatorTest.php PHP Fatal error: Call

    to a member function send() on null in /[…]/ AppBundle/Security/ GithubAuthenticator.php on line 43 Step 2 get the router
  115. 3 OBJECTS TO FAKE public function createToken(Request $request, $providerKey) {

    // … $url = $this->router->generate(‘admin’,[], true); $endpoint = ‘/login/oauth/access_token’; $guzzleRequest = $this->client->post($endpoint,[], [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'code' => $request->get('code'), 'redirect_uri' => $url ]); $response = $guzzleRequest->send(); $data = $response->json(); } 2 1 3 Step 3 fake data from Github
  116. public function createToken(Request $request, $providerKey) { // … $url =

    $this->router->generate(‘admin’,[], true); $endpoint = ‘/login/oauth/access_token’; $guzzleRequest = $this->client->post($endpoint,[], [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'code' => $request->get('code'), 'redirect_uri' => $url ]); $response = $request->send(); $data = $response->json(); } 1 Step 3 fake data from Github
  117. We need to create a dummy Guzzle\Http\Message \EntityEnclosingRequest $guzzleRequest 1

    Step 3 fake data from Github
  118. $guzzleRequestObjectProphecy = $this ->prophet ->prophesize(‘Guzzle\Http\Message\EntityEnclosingRequest') ; $guzzleRequest = $guzzleRequestObjectProphecy->reveal(); GET

    THE GUZZLE REQUEST DUMMY 1 Step 3 fake data from Github
  119. public function createToken(Request $request, $providerKey) { // … $url =

    $this->router->generate(‘admin’,[], true); $endpoint = ‘/login/oauth/access_token’; $guzzleRequest = $this->client->post($endpoint,[], [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'code' => $request->get('code'), 'redirect_uri' => $url ]); $response = $request->send(); $data = $response->json(); } 2 Step 3 fake data from Github
  120. We need to stub the call to the method $client->post(),

    it needs to return a $guzzleRequest 2 Step 3 fake data from Github
  121. $clientObjectProphecy = $this->prophet ->prophesize(‘Guzzle\Service\Client'); $clientObjectProphecy ->post('/login/oauth/access_token', [], [ 'client_id' =>

    ' ', 'client_secret' => ' ', 'code' => ' ', 'redirect_uri' => ' ' ]) ->willReturn($guzzleRequest) ; $client = $clientObjectProphecy->reveal(); STUB THE GUZZLECLIENT::POST 2
  122. public function createToken(Request $request, $providerKey) { // … $url =

    $this->router->generate(‘admin’,[], true); $endpoint = ‘/login/oauth/access_token’; $guzzleRequest = $this->client->post($endpoint,[], [ 'client_id' => $this->clientId, 'client_secret' => $this->clientSecret, 'code' => $request->get('code'), 'redirect_uri' => $url ]); $response = $guzzleRequest->send(); $data = $response->json(); } 3 Step 3 fake data from Github
  123. This method must return a: Guzzle\Http\Message\Response $response Let’s go for

    it! 3 Step 3 fake data from Github STUB THE GUZZLECLIENT::SEND
  124. $guzzleResponseObjectProphecy = $this->prophet ->prophesize('Guzzle\Http\Message\Response'); $guzzleResponse = $guzzleResponseObjectProphecy->reveal(); $guzzleRequestObjectProphecy ->send() ->willReturn($guzzleResponse)

    ; STUB THE GUZZLECLIENT::SEND 3
  125. None
  126. $token = $githubAuthenticator->createToken($request, ‘secure_area’); $this->assertSame( ‘a_fake_access_token', $token->getCredentials() ); $this->assertSame( ‘secure_area',

    $token->getProviderKey() ); $this->assertSame( ‘anon.', $token->getUser() ); $this->assertEmpty($token->getRoles()); $this->assertFalse($token->isAuthenticated()); $this->assertEmpty($token->getAttributes()); Test that the TOKEN is what we need to be. Step 4 assertions
  127. None
  128. Remember that output… Github returns an access token at that

    point.
  129. $guzzleResponseObjectProphecy = $this->prophet- >prophesize('Guzzle\Http\Message\Response'); $guzzleResponseObjectProphecy ->json() ->willReturn([‘access_token' => ‘a_fake_access_token']) ;

    $guzzleResponse = $guzzleResponseObjectProphecy->reveal(); Step 3 bis forgot thing (again?!)
  130. None
  131. $guzzleResponseObjectProphecy ->json() ->willReturn(array( ‘access_token' => ‘a_fake_access_token’ )) ->shouldBeCalledTimes(1) ; ADD

    EXPECTATION: MOCK Don’t expect to get a new assertion, as in PHPUnit
  132. public function tearDown() { $this->prophet->checkPredictions(); $this->prophet = null; } Mandatory

    DON’T FORGET ABOUT THE CHECK
  133. WRONG EXPECTATION $guzzleResponseObjectProphecy ->json() ->willReturn(array( ‘access_token' => ‘a_fake_access_token' )) ->shouldBeCalledTimes(10)

    ; But if the expectation is not right, you’ll get an exception.
  134. None
  135. None
  136. The Prophecy mock library philosophy is around the description of

    the future of an object double through a prophecy. Prophecy
  137. A prophecy must be revealed to get a dummy, a

    stub or a mock. Prophecy
  138. With PHPUnit, all is around the getMock() method. PHPUnit

  139. The first step is to get a mock, then describe

    the future of the double of the object. PHPUnit PHPUnit
  140. $prophet = new \Prophecy\Prophet; $prophecy = $prophet->prophesize(‘Foo\Bar’); $dummy = $prophecy->reveal();

    ——————————————————————————————————————— $dummy = $this->getMock(‘Foo\Bar’); PHPUnit Prophecy
  141. $prophecy ->send() ->willReturn($valueToReturn) ; —————————————————————————————————————————— $dummy ->method('send') ->willReturn($valueToReturn) ; Prophecy

    PHPUnit
  142. Extensible. ——————————————————————————————————————— Not extensible. Prophecy PHPUnit

  143. AWSOMNESS!

  144. None
  145. None
  146. Resources • https://github.com/phpspec/prophecy • https://phpunit.de/manual/4.5/en/test- doubles.html#test-doubles.prophecy • all the code

    is here: http://bit.ly/11pnp2I
  147. Thank you! @saro0h speakerdeck.com/saro0h/ saro0h This is a zero guys!