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
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
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!
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
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
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
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(); }
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 :)
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
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' ) }
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! ! ! ! !
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
Dummy Example class Comment { public function __construct($comment, User $user) { ... } public function validateComment() { //doesn't rely on User at all } } !
Dummy Example public function testValidateComment() { $user = $this->getMock('User'); $commentText = ""; $comment = new Comment($commentText,$user); $this->assertFalse($comment->validateComment()); } User fulfills the method signature, but doesn’t get used
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
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.
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!
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
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
Practical API Testing ✤ Generally, mocks suffice!! ✤ If the method is transforming data, stub it!! ✤ Spies are good to track multiple calls in same method
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(); }
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(); }