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

Mocking Dependencies in PHPUnit

61959992a4c7d18d7ac66902baaf0a82?s=47 Frost
May 23, 2014

Mocking Dependencies in PHPUnit

Mocking Dependencies in PHPUnit slides from PHP[tek] 2014

61959992a4c7d18d7ac66902baaf0a82?s=128

Frost

May 23, 2014
Tweet

Transcript

  1. Mocking Dependencies in PHPUnit Matt Frost · IRC: mfrost503 ·

    Feedback: http://joind.in/10643 · @shrtwhitebldguy!
  2. We’ll be covering ✤ Defining dependencies! ✤ Dependency Injection! ✤

    Test Doubles in Theory! ✤ Test Doubles in Practice
  3. What’s a dependency ✤ Unit Test Context! ✤ A unit(s)

    of code that adds functionality to another unit of code! ✤ Think system dependencies, but much smaller scale
  4. Why mock them? ✤ Unit tests should cover a single

    unit of code in isolation! ✤ A bug in a dependency makes your test a guessing game! ✤ We only want to know that the code we’re testing works
  5. None
  6. Dependencies in the wild class Auth! {! private $user;! public

    function __construct(User $user)! {! $this->user = $user;! }! ! public function authenticate()! {! $username = $this->user->getUserName();! $password = $this->user->getHash();! $this->checkLogin($username,$password);! }! } Dependency Alert!
  7. Don’t do this! class Auth! {! public function authenticate($username, $pass)!

    {! $user = new User($username, $pass);! $username = $user->getUserName();! $password = $user->getHash();! $this->checkLogin($username,$password);! } User cannot be mocked
  8. Dependency Injection ✤ Helps make code testable! ✤ Helps make

    code flexible! ✤ Constructor/Accessor methods
  9. MOAR Dependency Injection ✤ Dependencies become properties in the object

    in which they’re used! ✤ Paramount for mocking in unit tests!
  10. Mocking

  11. Defining Test Doubles ✤ Stand in for actual objects (think

    Stunt Doubles)! ✤ Can simulate functionality from those objects! ✤ Can fulfill the requirements of a type hinted method! ✤ Can be used to make sure a method on the mock is called
  12. A few more points ✤ Can’t directly mock private methods!

    ✤ Only mock what you need to test! ✤ In a pinch, Reflection API can help test private
  13. Theory ✤ Unit Test shouldn’t be dependent on external data

    source availability! ✤ Unit Test vs. Integration Test! ✤ “How do I know if my query is right?”! ✤ You’re testing code, not network availability
  14. Types of Test Doubles ✤ Mock! ✤ Stub! ✤ Dummy!

    ✤ Spy
  15. Mock ✤ Verifies that a method has been called correctly!

    ✤ Doesn’t generate a response
  16. Anatomy of a Mock ✤ Expectation! ✤ Method! ✤ Parameters

    (if applicable)
  17. Mock Example public function testSetUser() { $user = $this->getMock('User',array('setUserId')); $user->expects($this->once())

    ->method('setUserId') ->with(1); $post = new Post($user); $post->retrieve(10); $post->getUserInfo(); }
  18. Explanation ✤ Supposes $user->setUserId(1) will be called in the test!

    ✤ Fails if $user->setUserId(1) is not called
  19. Mock Implementation public function getUserInfo() { // assume $this->data is

    populated from // the $post->retrieve($id) method $userId = $this->data['user_id']; $this->user->setUserId($userId); return $this->user->retrieve(); } This is an example of code that would pass the previous test, it’s a fictional example...so I wouldn’t use the code :)
  20. Test Stub ✤ Ensures a method is a called correctly!

    ✤ Generates a “fake response” ! ✤ Response allows for different cases to be tested! ✤ Data Fixtures/Data Providers
  21. Data Providers public function NumberProvider() { array( array(2, 4), array(9,

    81), array(4, 16), array(12, 144), array(13, 169), array(5, 25), ) }
  22. Using a data provider /** * @dataProvider NumberProvider */ public

    function testSquares($integer, $value) { $mathOperations = new MathOperations(); $square = $mathOperations->square($integer); $this->assertEquals( $value, $square, 'Square value did not match expectation' ) }
  23. Response ✤ Declare a response for a particular case! ✤

    Doesn’t have to be a value, can throw Exceptions!! ✤ Can show if your code is behaving/failing correctly! ! ! ! !
  24. Stub Example public function testGetUserInfo() { $userInfo = array( 'first_name'

    => 'Joe', 'last_name' => 'Strummer', 'id' => 1, 'email' => 'joe.strummer@gmail.com' ); $user = $this->getMock('User', array('retrieve')); $user->expects($this->once()) ->method('retrieve') ->will($this->returnValue($userInfo)); ...
  25. Stub Example Cont’d ... $post = new Post($user); $post->retrieve(10); $information

    = $post->getUserInfo(); $this->assertEquals('Joe',$information['first_name']); $this->assertEquals('Strummer',$information['last_name']); } Here we’re asserting that retrieve is called correctly by validating that we get back what we expect
  26. Dummy ✤ It’s a place holder! ✤ It has no

    expectations or behavior! ✤ It satisfies a parameter list...
  27. Dummy Example <?php class Comment { public function __construct($comment, User

    $user) { ... } public function validateComment() { //doesn't rely on User at all } } !
  28. Dummy Example public function testValidateComment() { $user = $this->getMock('User'); $commentText

    = "<script></script>"; $comment = new Comment($commentText,$user); $this->assertFalse($comment->validateComment()); } User fulfills the method signature, but doesn’t get used
  29. Isolation! ✤ External Data Sources - don’t talk to em!!

    ✤ APIs! ✤ Database Responses
  30. Stubbing PDO ✤ Constructor is not serializable, we must adapt!!

    ✤ PDO::prepare - returns a PDO Statement (which we can stub)! ✤ We can easily cover a variety of outcomes
  31. Constructor <?php class PDOTestHelper extends PDO { public function __construct()

    { } } Overridden constructor allows us to mock!
  32. Setup/TearDown public function setUp() { $this->pdo = $this->getMock('PDOTestHelper', array(‘prepare’); $this->statement

    = $this->getMock('PDOStatement', array(‘execute’)); } ! public function tearDown() { unset($pdo); unset($statement); }
  33. Stubbing a prepared statement $this->pdo->expects($this->once()) ->method('prepare') ->with($this->stringContains('SELECT * from table'))

    ->will($this->returnValue($this->statement)) Prepare will return a PDOStatement when executed successfully, so in order to stub the preparation and execution of the query, this is how we need to start.
  34. Stubbing the execute call $this->statement->expects($this->once()) ->method('execute') ->with($this->isType('array')) ->will($this->returnValue($this->statement)); Since we’re

    expecting this call to succeed, we need to return the statement again. Once we get the statement back, we’ve successfully simulated the preparation and execution of a query!
  35. Stubbing Fetch! $simData = array( ‘id‘ => 1, ‘firstName‘ =>

    ‘Lloyd’, ‘lastName‘ => ‘Christmas’, ‘occupation‘ => ‘Dog Groomer’ ); $this->statement->expects($this->once()) ->method('fetch') ->will($this->returnValue($simData));
  36. Returning Data ✤ Data Fixtures! ✤ Data Providers! ✤ Data

    should resemble what you expect to get back
  37. Mocking API Calls ✤ Wrap it up, not just for

    testing for your own sanity!! ✤ Once it’s wrapped it can be mocked like anything else! ✤ Spies!! ✤ Don’t talk to the API
  38. Spies ✤ Helpful in making sure your method was called!

    ✤ Or called a certain number of times! ✤ Not commonly used, but I’ve found good use in testing APIs
  39. Practical API Testing ✤ Generally, mocks suffice!! ✤ If the

    method is transforming data, stub it!! ✤ Spies are good to track multiple calls in same method
  40. API Example public function testGetTweets() { //mock example $request =

    $this->getMock('Request',array('get')); $request->expects($this->once()) ->method('get') ->with('statuses'); $twitter = new Twitter($request); $twitter->getTweets(); }
  41. Spy Example public function testComplicatedMethod() { //spy example $request =

    $this->getMock('Request',array('get')); $request->expects($this->exactly(3)) ->method('get'); ! $twitter = new Twitter($request); $twitter->complicatedMethod(); }
  42. Helpful Tidbits - With() ✤ isType(String $type) - check by

    type! ✤ stringContains($value) - string parameter! ✤ contains($value) - array parameter! ✤ hasArrayKey($key)! ✤ greaterThan($value)! ✤ isInstanceOf($className)! ✤ matchesRegularExpression($pattern)! ✤ equalTo($value)
  43. Other options ✤ XPMock! ✤ Mockery! ✤ Phake

  44. XPMock ✤ Sits on top of PHPUnit! ✤ Less work

    to mock dependencies
  45. Example - PHPUnit <?php $mock = $this->getMockBuilder('MyClass') ->setMethods(['getBool', 'getNumber', 'getString'])

    ->disableOriginalConstructor() ->getMock(); $mock->expects($this->any()) ->method('getBool') ->will($this->returnValue(true)); $mock->expects($this->any()) ->method('getNumber') ->will($this->returnValue(1)); $mock->expects($this->any()) ->method('getString') ->will($this->returnValue('string'));
  46. Example XPMock $this->mock('MyClass') ->getBool(true) ->getNumber(1) ->getString('string') ->new();

  47. Mockery Example $service = m::mock('service'); $service->shouldReceive('readTemp')->times(3)->andReturn(10, 12, 14);

  48. Phake Example $this->item1 = Phake::mock('Item'); $this->item2 = Phake::mock('Item'); $this->item3 =

    Phake::mock('Item'); ! Phake::when($this->item1)->getPrice()->thenReturn(100); Phake::when($this->item2)->getPrice()->thenReturn(200); Phake::when($this->item3)->getPrice()->thenReturn(300);
  49. Summary ✤ Injected Dependencies = Increased Testability! ✤ Mock/Stub/Dummy! ✤

    Don’t do more than you need to!! ✤ Practice makes perfect
  50. Victory! Mocking effectively leads to better tests and better tests

    lead to better applications!
  51. Thank you! ✤ Freenode: mfrost503! ✤ Joind.in: http://joind.in/10643! ✤ Twitter:

    @shrtwhitebldguy