More Related Content

Recently uploaded(20)

Mocking Dependencies in PHPUnit

  1. Mocking Dependencies in PHPUnit Matt Frost · IRC: mfrost503 · Feedback:
  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. Mocking
  10. 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
  11. A few more points ✤ Can’t directly mock private or protected methods ✤ Only mock what you need to test ✤ In a pinch, Reflection API can help test private/protected
  12. 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
  13. Types of Test Doubles ✤ Mock ✤ Stub ✤ Dummy ✤ Spy
  14. Mock ✤ Verifies that a method has been called correctly ✤ Doesn’t generate a response
  15. Anatomy of a Mock ✤ Expectation ✤ Method ✤ Parameters (if applicable)
  16. 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(); }
  17. Explanation ✤ Supposes $user->setUserId(1) will be called in the test ✤ Fails if $user->setUserId(1) is not called
  18. 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 I wouldn’t use the code :)
  19. Test Stub ✤ Ensures a method is a called correctly ✤ Generates a “fake response” ✤ Response allows for different cases to be tested
  20. Response
  21. Stub Example public function testGetUserInfo() { $userInfo = array( 'first_name' => 'Joe', 'last_name' => 'Strummer', 'id' => 1, 'email' => '' ); $user = $this->getMock('User', array('retrieve')); $user->expects($this->once()) ->method('retrieve') ->will($this->returnValue($userInfo)); ...
  22. 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
  23. Dummy ✤ It’s a place holder ✤ It has no expectations or behavior ✤ It satisfies a parameter list...
  24. Dummy Example <?php class Comment { public function __construct($comment, User $user) { ... } public function validateComment() { //doesn't rely on User at all } }
  25. 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
  26. Practical Examples! ✤ External Data Sources - don’t talk to em! ✤ APIs ✤ Database Responses
  27. 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
  28. Constructor <?phpclass PDOTestHelper extends PDO{ public function __construct(){}} Overridden constructor allows us to mock!
  29. Setup/TearDown public function setUp() { $this->pdo = $this->getMock('PDOTestHelper'); $this->statement = $this->getMock('PDOStatement'); } public function tearDown() { unset($pdo); unset($statement); }
  30. 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.
  31. 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!
  32. Stubbing Fetch! $simData = array( ‘id‘ => 1, ‘firstName‘ => ‘Lloyd’, ‘lastName‘ => ‘Christmas’, ‘occupation‘ => ‘Dog Groomer’ ); $this->statement->expects($this->once()) ->method('fetch') ->will($this->returnValue($simData));
  33. Returning Data ✤ Data Fixtures ✤ Data Providers ✤ Data should resemble what you expect to get back
  34. 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
  35. 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
  36. Practical API Testing ✤ Generally, mocks suffice! ✤ If the method is transforming data, stub it! ✤ Spies are good to track multiple calls in same method
  37. 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(); }
  38. 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(); }
  39. 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)
  40. Summary ✤ Injected Dependencies = Increased Testability ✤ Mock/Stub/Dummy ✤ Don’t do more than you need to! ✤ Practice makes perfect
  41. Victory! Mocking effectively leads to better tests and better tests lead to better applications!
  42. Thank you! ✤ Freenode: mfrost503 ✤