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

Mocking Dependencies in PHPUnit

Sponsored · Your Podcast. Everywhere. Effortlessly. Share. Educate. Inspire. Entertain. You do you. We'll handle the rest.
Avatar for Frost Frost
May 23, 2014

Mocking Dependencies in PHPUnit

Mocking Dependencies in PHPUnit slides from PHP[tek] 2014

Avatar for Frost

Frost

May 23, 2014
Tweet

More Decks by Frost

Other Decks in Technology

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. 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!
  6. 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
  7. Dependency Injection ✤ Helps make code testable! ✤ Helps make

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

    in which they’re used! ✤ Paramount for mocking in unit tests!
  9. 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
  10. 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
  11. 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
  12. 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(); }
  13. Explanation ✤ Supposes $user->setUserId(1) will be called in the test!

    ✤ Fails if $user->setUserId(1) is not called
  14. 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 :)
  15. 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
  16. Data Providers public function NumberProvider() { array( array(2, 4), array(9,

    81), array(4, 16), array(12, 144), array(13, 169), array(5, 25), ) }
  17. 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' ) }
  18. 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! ! ! ! !
  19. Stub Example public function testGetUserInfo() { $userInfo = array( 'first_name'

    => 'Joe', 'last_name' => 'Strummer', 'id' => 1, 'email' => '[email protected]' ); $user = $this->getMock('User', array('retrieve')); $user->expects($this->once()) ->method('retrieve') ->will($this->returnValue($userInfo)); ...
  20. 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
  21. Dummy ✤ It’s a place holder! ✤ It has no

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

    $user) { ... } public function validateComment() { //doesn't rely on User at all } } !
  23. 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
  24. 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
  25. 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); }
  26. 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.
  27. 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!
  28. Stubbing Fetch! $simData = array( ‘id‘ => 1, ‘firstName‘ =>

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

    should resemble what you expect to get back
  30. 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
  31. 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
  32. Practical API Testing ✤ Generally, mocks suffice!! ✤ If the

    method is transforming data, stub it!! ✤ Spies are good to track multiple calls in same method
  33. 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(); }
  34. 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(); }
  35. 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)
  36. 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'));
  37. 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);
  38. Summary ✤ Injected Dependencies = Increased Testability! ✤ Mock/Stub/Dummy! ✤

    Don’t do more than you need to!! ✤ Practice makes perfect