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
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!
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);
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
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();
}
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 example...so 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
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...
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
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
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!
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();
}