Reputation: 678
I a unit-testing beginner and seem to be stuck on how to unit test a function that contains an internal function call. For example, testing the following:
public function getBlueFooCount($fooId) {
$foo = $fooDao->getFoosById($fooId);
// custom logic to pull out the blue foos from result set
// custom count business logic on blue foos ...
return $count;
}
How would I be able to mock what the internal function retrieves? Is this because the function is too tightly coupled and it needs to be loosened?
Upvotes: 2
Views: 1533
Reputation: 4029
To build off @aib's answer, when we started to unit test our legacy code base the majority of our classes were very tightly coupled and many methods were instantiating new objects themselves. While we've taken steps on implementing Dependency Injection and Inversion of Control going forward, we were still stuck with hundreds of classes that still needed unit tested.
When writing a unit test for a legacy method, we refactored and pulled the instantiation out into a new small method we could then stub. Not the best pattern, but it was cheap and got the job done without having to do a larger refactoring.
class FooCounter {
public function getFooDao(){
return new FooDao();
}
public function getBlueFooCount($fooId) {
/* was this
$fooDao = new FooDao();
*/
$foo = $this->getFooDao()->getFoosById($fooId);
// custom logic to pull out the blue foos from result set
// custom count business logic on blue foos ...
return $count;
}
}
class FooCounterTest extends PHPUnit_Framework_TestCase {
public function test_getBlueFooCount(){
$fooCounter = $this->getMock('FooCounter', array('getFooDao'));
$fooCounter->expects($this->any())
->method('getFooDao')
->will($this->returnValue(new MockFooDao()));
$this->assertEquals(0, $fooCounter->getBlueFooCount(1));
}
}
If we were implementing a new class, we typically are using constructor based DI and it is the answer I will give if you are creating something new. Here's a link by someone else because I trust it's been said better before (sort of DRY): Dependency Injection and Unit Testing. And some examples of each for your case:
Constructor based injection
class FooCounter {
private $_fooDao
public function __construct($fooDao){
$this->_fooDao = $fooDao
}
public function getBlueFooCount($fooId) {
$foo = $this->_fooDao->getFoosById($fooId);
// custom logic to pull out the blue foos from result set
// custom count business logic on blue foos ...
return $count;
}
}
class FooCounterTest extends PHPUnit_Framework_TestCase {
public function test_getBlueFooCount(){
$fooCounter = new FooCounter(new MockFooDao());
$this->assertEquals(0, $fooCounter->getBlueFooCount(1));
}
}
Setter based injection
class FooCounter {
private $_fooDao
public function setFooDao($fooDao){
$this->_fooDao = $fooDao
}
public function getBlueFooCount($fooId) {
$foo = $this->_fooDao->getFoosById($fooId);
// custom logic to pull out the blue foos from result set
// custom count business logic on blue foos ...
return $count;
}
}
class FooCounterTest extends PHPUnit_Framework_TestCase {
public function test_getBlueFooCount(){
$fooCounter = new FooCounter();
$fooCounter->setFooDao(new MockFooDao());
$this->assertEquals(0, $fooCounter->getBlueFooCount(1));
}
}
Upvotes: 2
Reputation: 47031
You need to mock $fooDao
. Hopefully there's a setter or a DI container and you're not creating it with new
(in which case it might be too tightly coupled.)
Upvotes: 2