jexx2345
jexx2345

Reputation: 678

Testing a function that has an inner function call

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

Answers (2)

Paul DelRe
Paul DelRe

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

aib
aib

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

Related Questions