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
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
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
<?phpclass 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 ✤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 thewild 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! classAuth{ 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 ✤ Helpsmake 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.
  • 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 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 Testshouldn’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 TestDoubles ✤ Mock ✤ Stub ✤ Dummy ✤ Spy
  • 14.
    Mock ✤ Verifies thata method 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)will be 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 ✤ Ensuresa method 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 aplace holder ✤ 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! ✤ ExternalData Sources - don’t talk to em! ✤ APIs ✤ Database Responses
  • 27.
    Stubbing PDO ✤ Constructoris 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 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 ✤ DataFixtures ✤ 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 inmaking 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 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.
    Thank you! ✤ Freenode:mfrost503 ✤ Joind.in: http://joind.in/8693