mocking dependencies in phpunit

42
Mocking Dependencies in PHPUnit Matt Frost · IRC: mfrost503 · Feedback: http://joind.in/8693

Upload: mfrost503

Post on 09-Jul-2015

386 views

Category:

Technology


3 download

TRANSCRIPT

Page 1: Mocking Dependencies in PHPUnit

Mocking Dependencies in PHPUnit

Matt Frost · IRC: mfrost503 · Feedback: http://joind.in/8693

Page 2: Mocking Dependencies in PHPUnit

We’ll be covering

✤ Defining dependencies

✤ Dependency Injection

✤ Test Doubles in Theory

✤ Test Doubles in Practice

Page 3: Mocking Dependencies in PHPUnit

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

Page 4: Mocking Dependencies in PHPUnit

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

Page 5: Mocking Dependencies in PHPUnit

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!

Page 6: Mocking Dependencies in PHPUnit

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

Page 7: Mocking Dependencies in PHPUnit

Dependency Injection

✤ Helps make code testable

✤ Helps make code flexible

✤ Constructor/Accessor methods

Page 8: Mocking Dependencies in PHPUnit

MOAR Dependency Injection

✤ Dependencies become properties in the object in which they’re used

✤ Paramount for mocking in unit tests!

Page 9: Mocking Dependencies in PHPUnit

Mocking

Page 10: Mocking Dependencies in PHPUnit

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

Page 11: Mocking Dependencies in PHPUnit

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

Page 12: Mocking Dependencies in PHPUnit

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

Page 13: Mocking Dependencies in PHPUnit

Types of Test Doubles

✤ Mock

✤ Stub

✤ Dummy

✤ Spy

Page 14: Mocking Dependencies in PHPUnit

Mock

✤ Verifies that a method has been called correctly

✤ Doesn’t generate a response

Page 15: Mocking Dependencies in PHPUnit

Anatomy of a Mock

✤ Expectation

✤ Method

✤ Parameters (if applicable)

Page 16: Mocking Dependencies in PHPUnit

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(); }

Page 17: Mocking Dependencies in PHPUnit

Explanation

✤ Supposes $user->setUserId(1) will be called in the test

✤ Fails if $user->setUserId(1) is not called

Page 18: Mocking Dependencies in PHPUnit

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 :)

Page 19: Mocking Dependencies in PHPUnit

Test Stub

✤ Ensures a method is a called correctly

✤ Generates a “fake response”

✤ Response allows for different cases to be tested

Page 20: Mocking Dependencies in PHPUnit

Response

✤ Literally declaring what the response will be

✤ Doesn’t have to be a value, can throw Exceptions!

✤ Can show if your code is behaving/failing correctly

Page 21: Mocking Dependencies in PHPUnit

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)); ...

Page 22: Mocking Dependencies in PHPUnit

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

Page 23: Mocking Dependencies in PHPUnit

Dummy

✤ It’s a place holder

✤ It has no expectations or behavior

✤ It satisfies a parameter list...

Page 24: Mocking Dependencies in PHPUnit

Dummy Example

<?phpclass Comment{ public function __construct($comment, User $user) { ... } public function validateComment() { //doesn't rely on User at all }}

Page 25: Mocking Dependencies in PHPUnit

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

Page 26: Mocking Dependencies in PHPUnit

Practical Examples!

✤ External Data Sources - don’t talk to em!

✤ APIs

✤ Database Responses

Page 27: Mocking Dependencies in PHPUnit

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

Page 28: Mocking Dependencies in PHPUnit

Constructor

<?phpclass PDOTestHelper extends PDO{ public function __construct() { }}

Overridden constructor allows us to mock!

Page 29: Mocking Dependencies in PHPUnit

Setup/TearDown

public function setUp(){ $this->pdo = $this->getMock('PDOTestHelper'); $this->statement = $this->getMock('PDOStatement');}public function tearDown(){ unset($pdo); unset($statement);}

Page 30: Mocking Dependencies in PHPUnit

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.

Page 31: Mocking Dependencies in PHPUnit

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!

Page 32: Mocking Dependencies in PHPUnit

Stubbing Fetch!

$simData = array( ‘id‘ => 1, ‘firstName‘ => ‘Lloyd’, ‘lastName‘ => ‘Christmas’, ‘occupation‘ => ‘Dog Groomer’ ); $this->statement->expects($this->once()) ->method('fetch') ->will($this->returnValue($simData));

Page 33: Mocking Dependencies in PHPUnit

Returning Data

✤ Data Fixtures

✤ Data Providers

✤ Data should resemble what you expect to get back

Page 34: Mocking Dependencies in PHPUnit

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

Page 35: Mocking Dependencies in PHPUnit

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

Page 36: Mocking Dependencies in PHPUnit

Practical API Testing

✤ Generally, mocks suffice!

✤ If the method is transforming data, stub it!

✤ Spies are good to track multiple calls in same method

Page 37: Mocking Dependencies in PHPUnit

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(); }

Page 38: Mocking Dependencies in PHPUnit

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(); }

Page 39: Mocking Dependencies in PHPUnit

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)

Page 40: Mocking Dependencies in PHPUnit

Summary

✤ Injected Dependencies = Increased Testability

✤ Mock/Stub/Dummy

✤ Don’t do more than you need to!

✤ Practice makes perfect

Page 41: Mocking Dependencies in PHPUnit

Victory!

Mocking effectively leads to better tests and better tests lead to better applications!

Page 42: Mocking Dependencies in PHPUnit

Thank you!

✤ Freenode: mfrost503

✤ Joind.in: http://joind.in/8693