SymfonyCon Madrid 2014 - PHP object mocking framework world

34ade09dd3d11004ca8ee4174fd3d6a2?s=47 Sarah KHALIL
November 27, 2014
1.1k

SymfonyCon Madrid 2014 - 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

November 27, 2014
Tweet

Transcript

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

    Sarah Khalil - @saro0h
  2. TODAY, HOPEFULLY, WE LEARN NEW THINGS 1. Terminology about objects

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

  4. DUMMIES

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

    They are usually used to fill a list of parameters.
  6. STUB

  7. Stubs are objects that implement the same methods than the

    real object. These methods do nothing and are configured to return a specific value.
  8. MOCK

  9. Mocks are pre-programmed with expectations which form 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.
  10. PHPUNIT MOCKING LIBRARY

  11. INSTALL IT

  12. FUNDAMENTALS

  13. Your test class must extend PHPUnit_Framework_TestCase

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

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

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

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

  18. MOCK $dispatcher = $this->getMock(‘Symfony\Component \EventDispatcher\EventDispatcherInterface'); ! $dispatcher ->expects($this->once()) ->method(‘dispatch') ->with(

    $this->equalTo(SecurityEvents::INTERACTIVE_LOGIN), $this->equalTo($loginEvent) ) ;
  19. REAL LIFE EXAMPLE

  20. None
  21. None
  22. Who knows exactly how the Oauth dance of Github works?

    Don’t worry, we don’t care!
  23. THE ORIGINAL CODE

  24. namespace PoleDev\AppBundle\Security;! ! use Guzzle\Service\Client;! use Psr\Log\LoggerInterface;! 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…! }
  25. private $client; private $router; private $logger; ! public function __construct(

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

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

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

    = ‘/login/oauth/access_token’; ! $request = $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();
  29. STEP 2: 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); }
  30. STEP 3: CREATE TOKEN return new PreAuthenticatedToken( 'anon.', $data[‘access_token’], $providerKey

    );
  31. LET’S TEST IT!

  32. WHAT TO TEST?

  33. 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 this method
  34. CREATE THE TEST CLASS

  35. namespace PoleDev\AppBundle\Tests\Security; ! class GithubAuthenticatorTest extends \PHPUnit_Framework_TestCase { public function

    testCreateToken() { } }
  36. LET’S CALL THE CODE WE NEED TO TEST

  37. LET’S GET OUR DUMMIES AND CALL OUR METHOD TO TEST

    public function testCreateToken() { $githubAuthenticator = new GithubAuthenticator( $client, $router, $logger, '', '' ); ! $token = $githubAuthenticator ->createToken($request, ‘secure_area') ; }
  38. To construct the GithubAuthenticator, we need: • Guzzle\Service\Client • Symfony\Bundle\FrameworkBundle\Routing\Router

    • Psr\Log\LoggerInterface • $clientId = ‘’ • $clientSecret = ‘’
  39. To call the createToken() method, we need ! • Symfony\Component\HttpFoundation\Request

    • $providerKey = 'secure_area'
  40. $client = $this->getMock(‘Guzzle\Service \Client’); ! $router = $this->getMock('Symfony\Bundle \FrameworkBundle\Routing\Router'); !

    $logger = $this->getMock('Psr\Log \LoggerInterface'); ! $request = $this->getMock('Symfony\Component \HttpFoundation\Request'); This a dummy
  41. None
  42. We must disable the use of the original Router constructor

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

  44. MacBook-Pro-de-Sarah:~/Documents/talks/ symfonycon-madrid-2014/code-exemple$ phpunit - c app/ src/PoleDev/AppBundle/Tests/Security/ GithubAuthenticatorTest.php ! !

    PHPUnit 4.3.5 by Sebastian Bergmann.! Configuration read from /Users/saro0h/ Documents/talks/symfonycon-madrid-2014/code- exemple/app/phpunit.xml.dist! ! PHP Fatal error: Call to a member function send() on null in /Users/saro0h/Documents/ talks/symfonycon-madrid-2014/code-exemple/src/ PoleDev/AppBundle/Security/ GithubAuthenticator.php on line 43
  45. STEP 1: GET ACCESS TOKEN $url = $this->router->generate(‘admin’,[], true); $endpoint

    = ‘/login/oauth/access_token’; ! $request = $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();
  46. 1/ We need to stub the call to the method

    $router->generate() it needs to return an url
  47. ! ! $router->method('generate')! ->with('admin',[], true)! ->willReturn(‘http://domain.name')! ;!

  48. 2/ We need to create a dummy Guzzle\Http\Message \EntityEnclosingRequest $guzzleRequest

  49. $guzzleRequest = $this! ! ! ->getMockBuilder('Guzzle\Http \Message\EntityEnclosingRequest')! ->disableOriginalConstructor()! ->getMock()! ;

  50. 3/ We need to stub the call to the method

    $client- >post(), it needs to return a $guzzleRequest
  51. $endpoint = '/login/oauth/access_token';! ! $client->method('post')! ->with($endpoint, [], [! 'client_id' =>

    '',! 'client_secret' => '',! 'code' => '',! 'redirect_uri' => 'http://domain.name'! ])! ->willReturn($guzzleRequest)! ;
  52. ORIGINAL CODE (STEP 1: GET ACCESS TOKEN) $url = $this->router->generate(‘admin’,[],

    true); $endpoint = ‘/login/oauth/access_token’; ! $request = $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();
  53. Create a stub for $request->send() This method must return a:

    Guzzle\Http\Message\Response $response ! Let’s go for it!
  54. $guzzleResponse = $this->getMockBuilder('Guzzle\Http \Message\Response') ->disableOriginalConstructor() ->getMock() ; ! $guzzleRequest ->method(‘send')

    ->willReturn($guzzleResponse) ;
  55. Hurray! The original code is running with our dummies and

    stubs. But we do not test anything.
  56. A LITTLE REMINDER

  57. 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 this method
  58. TEST THAT THE TOKEN IS WHAT WE NEED TO BE

    $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());! !
  59. None
  60. 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.
  61. $guzzleResponse! ->method('json')! ->willReturn([! ‘access_token' => ! ‘a_fake_access_token’! ])! ;

  62. Hurray! The original code is running with our dummies and

    stubs. And it is tested !
  63. USING A MOCK THIS TIME $guzzleResponse! ->expects($this->once())! ->method('json')! ->willReturn([! ‘access_token'

    => ‘a_fake_access_token'! ])! ; Expectation create a new assertion.
  64. PROPHECY

  65. INSTALL IT

  66. FUNDAMENTALS

  67. PROPHET

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

  69. DUMMY

  70. DUMMY $routerObjectProphecy = $prophet ->prophesize(‘Symfony\Bundle\FrameworkBundle\Routing\Router') ; ! $router = $routerObjectProphecy->reveal();

    dummy
  71. PROPHECY

  72. PROPHECY • Object used to describe the future of your

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

  74. OBJECT DOUBLE The goal is to get the object double

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

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

  77. PROMISE

  78. • willReturn() => is actually a promise.

  79. A promise is a piece of code allowing that a

    method call with a certain argument (if there is one), returns always the same value.
  80. $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:
  81. NOT ENOUGH? Implement the Prophecy\Promise\PromiseInterface

  82. ARGUMENT

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

  84. 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()
  85. $prophecy ->myMethod(Prophecy\Argument::any()) ;

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

    with this https://github.com/phpspec/prophecy#arguments-wildcarding =>
  87. MOCK

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

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

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

  91. NOT ENOUGH? Implement the Prediction\PredictionInterface

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

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

  94. SPIES

  95. 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
  96. REAL LIFE EXAMPLE

  97. None
  98. None
  99. THE ORIGINAL CODE You don’t remember right…

  100. namespace PoleDev\AppBundle\Security;! ! use Guzzle\Service\Client;! use Psr\Log\LoggerInterface;! 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…! }
  101. private $client; private $router; private $logger; ! public function __construct(

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

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

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

    = ‘/login/oauth/access_token’; ! $request = $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();
  105. STEP 2: 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); }
  106. STEP 3: CREATE TOKEN return new PreAuthenticatedToken( 'anon.', $data[‘access_token’], $providerKey

    );
  107. LET’S TEST IT! AGAIN !

  108. WHAT TO TEST?

  109. 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 this method
  110. CREATE THE TEST CLASS

  111. namespace PoleDev\AppBundle\Tests\Security; ! class GithubAuthenticatorTest extends \PHPUnit_Framework_TestCase { public function

    testCreateToken() { } }
  112. 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; } }
  113. LET’S GET OUR DUMMIES AND CALL OUR METHOD TO TEST

    public function testCreateToken() { $githubAuthenticator = new GithubAuthenticator( $client, $router, $logger, '', '' ); ! $token = $githubAuthenticator ->createToken($request, ‘secure_area') ; }
  114. To construct the GithubAuthenticator, we need • Guzzle\Service\Client • Symfony\Bundle\FrameworkBundle\Routing\Router

    • Psr\Log\LoggerInterface • $clientId = ‘’ • $clientSecret = ‘’
  115. To call the createToken() method, we need ! • Symfony\Component\HttpFoundation\Request

    • $providerKey = 'secure_area'
  116. 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
  117. $routerObjectProphecy = $this->prophet ->prophesize('Symfony\Bundle\FrameworkBundle \Routing\Router'); $router = $routerObjectProphecy->reveal(); ! $loggerObjectProphecy

    = $this->prophet ->prophesize('Psr\Log\LoggerInterface'); $logger = $loggerObjectProphecy->reveal(); ! $requestObjectProphecy = $this->prophet ->prophesize('Symfony\Component\HttpFoundation \Request'); $request = $requestObjectProphecy->reveal();
  118. MacBook-Pro-de-Sarah:~/Documents/talks/ symfonycon-madrid-2014/code-exemple$ phpunit - c app/ src/PoleDev/AppBundle/Tests/Security/ GithubAuthenticatorTest.php ! !

    PHPUnit 4.3.5 by Sebastian Bergmann.! Configuration read from /Users/saro0h/ Documents/talks/symfonycon-madrid-2014/code- exemple/app/phpunit.xml.dist! ! PHP Fatal error: Call to a member function send() on null in /Users/saro0h/Documents/ talks/symfonycon-madrid-2014/code-exemple/src/ PoleDev/AppBundle/Security/ GithubAuthenticator.php on line 43
  119. The null object expected is a: Guzzle\Http\Message\EntityEnclosingRequest $request ! !

    Let’s provide it!
  120. $guzzleRequestObjectProphecy = $this ->prophet ->prophesize(‘Guzzle\Http\Message\EntityEnclosingRequest') ; ! $guzzleRequest = $guzzleRequestObjectProphecy->reveal();

  121. ORIGINAL CODE (STEP 1: GET ACCESS TOKEN) $url = $this->router->generate(‘admin’,[],

    true); $endpoint = ‘/login/oauth/access_token’; ! $request = $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();
  122. $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();
  123. ORIGINAL CODE (STEP 1: GET ACCESS TOKEN) $url = $this->router->generate(‘admin’,[],

    true); $endpoint = ‘/login/oauth/access_token’; ! $request = $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();
  124. Create a stub for $request->send() This method must return a:

    Guzzle\Http\Message\Response $response ! Let’s go for it!
  125. $guzzleResponseObjectProphecy = $this->prophet ->prophesize('Guzzle\Http\Message\Response'); $guzzleResponse = $guzzleResponseObjectProphecy->reveal(); ! $guzzleRequestObjectProphecy =

    $this ->prophet ->prophesize(‘Guzzle\Http\Message\EntityEnclosingRequest') ; ! $guzzleRequestObjectProphecy ->send() ->willReturn($guzzleResponse) ; ! $guzzleRequest = $guzzleRequestObjectProphecy->reveal();
  126. Hurray! The original code is running with our dummies and

    stubs. But we do not test anything.
  127. WHAT WE NEED TO TEST AGAIN?

  128. 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 this method
  129. TEST THAT THE TOKEN IS WHAT WE NEED TO BE

    $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());! !
  130. None
  131. Remember that output… ! Github returns an access token at

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

    ; ! $guzzleResponse = $guzzleResponseObjectProphecy->reveal();
  133. Hurray! The original code is running with our dummies and

    stubs. And it is tested !
  134. USING A MOCK THIS TIME ! $guzzleResponseObjectProphecy ->json() ->willReturn(array(‘access_token' =>

    'a_fake_access_token')) ->shouldBeCalledTimes(1) ; Don’t expect to get a new assertion, as in PHPUnit
  135. public function tearDown()! {! $this->prophet->checkPredictions();! $this->prophet = null;! } Mandatory

    DON’T FORGET ABOUT THE CHECK
  136. WRONG EXPECTATION But if the expectation is not right, you’ll

    get an exception. ! $guzzleResponseObjectProphecy ->json() ->willReturn(array(‘access_token' => 'a_fake_access_token')) ->shouldBeCalledTimes(10) ;
  137. None
  138. TO SUM UP ALL OF THIS

  139. The Prophecy mock library philosophy is around the description of

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

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

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

    the future of the double of the object. PHPUnit PHPUnit
  143. $dummy = $this->getMock(‘Foo\Bar’); ! ——————————————————————————————————————— ! $prophet = new \Prophecy\Prophet;

    $prophecy = $prophet->prophesize(‘Foo\Bar’); $dummy = $prophecy->reveal(); PHPUnit Prophecy
  144. $prophecy ->send() ->willReturn($valueToReturn) ; —————————————————————————————————————————— $dummy ->method('send') ->willReturn($valueToReturn) ; Prophecy

    PHPUnit
  145. Extensible. ! ——————————————————————————————————————— ! Not extensible. Prophecy PHPUnit

  146. AWSOMNESS!

  147. None
  148. 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
  149. Thank you! @saro0h joind.in/talk/view/12957 sarah-khalil.com/talks saro0h