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

Mocking Dependencies in PHPUnit

Frost
May 23, 2014

Mocking Dependencies in PHPUnit

Mocking Dependencies in PHPUnit slides from PHP[tek] 2014

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