Mocking Dependencies in
PHPUnit
Matt Frost · IRC: mfrost503 · Feedback: http://joind.in/8693
We’ll be covering
✤

Defining dependencies

✤

Dependency Injection

✤

Test Doubles in Theory

✤

Test Doubles in Practice
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
Dependency Alert!
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);
}
}
Don’t do this!

class Auth
{ public function authenticate($username,User cannot be
$pass)
{ $user = new User($username, $pass);
mocked
$username = $user->getHash();
= $user->getUserName();
$password
} $this->checkLogin($username,$password);
Dependency Injection
✤

Helps make code testable

✤

Helps make code flexible

✤

Constructor/Accessor methods
MOAR Dependency Injection

✤

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

✤

Paramount for mocking in unit tests!
Mocking
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
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
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
Types of Test Doubles

✤

Mock

✤

Stub

✤

Dummy

✤

Spy
Mock
✤

Verifies that a method has been called correctly

✤

Doesn’t generate a response
Anatomy of a Mock
✤

Expectation

✤

Method

✤

Parameters (if applicable)
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();
}
Explanation

✤

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

✤

Fails if $user->setUserId(1) is not called
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
Response
Stub Example
public function testGetUserInfo()
{
$userInfo = array(
'first_name' => 'Joe',
'last_name' => 'Strummer',
'id' => 1,
'email' => 'joe.strummer@gmail.com'
);
$user = $this->getMock('User', array('retrieve'));
$user->expects($this->once())
->method('retrieve')
->will($this->returnValue($userInfo));
...
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
✤

It’s a place holder

✤

It has no expectations or behavior

✤

It satisfies a parameter list...
Dummy Example

<?php
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 = "<script></script>";
$comment = new Comment($commentText,$user);
$this->assertFalse($comment->validateComment());
}

User fulfills the method signature, but doesn’t get used
Practical Examples!
✤

External Data Sources - don’t talk to em!

✤

APIs

✤

Database Responses
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
Constructor

<?php
class PDOTestHelper extends PDO
{ public function __construct()
{
} }
Overridden constructor
allows us to mock!
Setup/TearDown

public function setUp()
{ $this->pdo = $this->getMock('PDOTestHelper');
$this->statement = $this->getMock('PDOStatement');
}
public function tearDown()
{ unset($pdo);
} unset($statement);
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!
Stubbing Fetch!

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

Data Fixtures

✤

Data Providers

✤

Data should resemble what you expect to get back
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();
}
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)
Summary
✤

Injected Dependencies = Increased Testability

✤

Mock/Stub/Dummy

✤

Don’t do more than you need to!

✤

Practice makes perfect
Victory!

Mocking effectively leads to better tests and better tests lead to
better applications!
Thank you!
✤

Freenode: mfrost503

✤

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

Mocking Dependencies in PHPUnit

  • 1.
    Mocking Dependencies in PHPUnit MattFrost · IRC: mfrost503 · Feedback: http://joind.in/8693
  • 2.
    We’ll be covering ✤ Definingdependencies ✤ Dependency Injection ✤ Test Doubles in Theory ✤ Test Doubles in Practice
  • 3.
    What’s a dependency ✤ UnitTest 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? ✤ Unittests 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 thewild Dependency Alert! 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); } }
  • 6.
    Don’t do this! classAuth { public function authenticate($username,User cannot be $pass) { $user = new User($username, $pass); mocked $username = $user->getHash(); = $user->getUserName(); $password } $this->checkLogin($username,$password);
  • 7.
    Dependency Injection ✤ Helps makecode testable ✤ Helps make code flexible ✤ Constructor/Accessor methods
  • 8.
    MOAR Dependency Injection ✤ Dependenciesbecome properties in the object in which they’re used ✤ Paramount for mocking in unit tests!
  • 9.
  • 10.
    Defining Test Doubles ✤ Standin 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 morepoints ✤ 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’tbe 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 TestDoubles ✤ Mock ✤ Stub ✤ Dummy ✤ Spy
  • 14.
    Mock ✤ Verifies that amethod has been called correctly ✤ Doesn’t generate a response
  • 15.
    Anatomy of aMock ✤ Expectation ✤ Method ✤ Parameters (if applicable)
  • 16.
    Mock Example public functiontestSetUser() { $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) willbe called in the test ✤ Fails if $user->setUserId(1) is not called
  • 18.
    Mock Implementation public functiongetUserInfo() { // 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 :)
  • 19.
    Test Stub ✤ Ensures amethod is a called correctly ✤ Generates a “fake response” ✤ Response allows for different cases to be tested
  • 20.
  • 21.
    Stub Example public functiontestGetUserInfo() { $userInfo = array( 'first_name' => 'Joe', 'last_name' => 'Strummer', 'id' => 1, 'email' => 'joe.strummer@gmail.com' ); $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 placeholder ✤ It has no expectations or behavior ✤ It satisfies a parameter list...
  • 24.
    Dummy Example <?php class Comment { publicfunction __construct($comment, User $user) { ... } public function validateComment() { //doesn't rely on User at all } }
  • 25.
    Dummy Example public functiontestValidateComment() { $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 DataSources - don’t talk to em! ✤ APIs ✤ Database Responses
  • 27.
    Stubbing PDO ✤ Constructor isnot serializable, we must adapt! ✤ PDO::prepare - returns a PDO Statement (which we can stub) ✤ We can easily cover a variety of outcomes
  • 28.
    Constructor <?php class PDOTestHelper extendsPDO { 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 preparedstatement $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 executecall $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 ✤ DataProviders ✤ Data should resemble what you expect to get back
  • 34.
    Mocking API Calls ✤ Wrapit 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 makingsure 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 functiontestGetTweets() { //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 functiontestComplicatedMethod() { //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 leadsto better tests and better tests lead to better applications!
  • 42.