Jay
Jay

Reputation: 45

PHPUnit Test How Many Times A Function Is Called

I'm working on a test in phpunit and I'm running into an issue. I have a public function on my class that I am trying to test. Depending on the parameters passed in to the method, a protected function also in my test class will be called one or two times. I currently have a test in place to check that the return data is correct, but I would also like to make sure the protected method is being called the correct number of times.

I know that a mock object will allow me to count the number of times a function is called, but it will also override the value returned by the protected function. I tried using a mock object with no "will" section, but it would just return null, not the actual value for the protected method.

ExampleClass

public function do_stuff($runTwice){
$results = do_cool_stuff();
  if($runTwice){
    $results = 2 * do_cool_stuff();
  }
  return $results;
}

protected function do_cool_stuff()
{
  return 2;
} 

In my test, I want to check whether do_cool_stuff() was called once or twice, but I still want the return values of both functions to be the same so I can test those as well in my unit test.

tl;dr I want to count the number of times a protected method in my test object is called (like you can do with a mock object) but I still want all the methods in my test method to return their normal values (not like a mock object).

Upvotes: 3

Views: 3558

Answers (2)

Michael Eakins
Michael Eakins

Reputation: 4179

Try setting a global variable prior to utilizing the class.

$IAmDeclaredOutsideOfTheFunction;

then use it to store the count and simply check it after your functions and classes have been called.

Upvotes: 1

Ian
Ian

Reputation: 977

Alternatively, revert back to rolling your own testable stand-in. The following aint pretty, but you get the idea:

class ExampleClass {
    public function do_stuff($runTwice) {
        $results = $this->do_cool_stuff();
        if ($runTwice) {
            $results = 2 * $this->do_cool_stuff();
        }
        return $results;
    }

    protected function do_cool_stuff() {
        return 2;
    }
}

class TestableExampleClass extends ExampleClass {
    /** Stores how many times the do_cool_stuff method is called */
    protected $callCount;

    function __construct() {
        $this->callCount = 0;
    }

    function getCallCount() {
        return $this->callCount;
    }

    /** Increment the call counter, and then use the base class's functionality */
    protected function do_cool_stuff() {
        $this->callCount++;
        return parent::do_cool_stuff();
    }
}


class ExampleClassTest extends PHPUnit_Framework_TestCase {

    public function test_do_stuff() {
        $example = new ExampleClass();
        $this->assertEquals(2, $example->do_stuff(false));
        $this->assertEquals(4, $example->do_stuff(true));
    }

    public function test_do_cool_stuff_is_called_correctly() {
        // Try it out the first way
        $firstExample = new TestableExampleClass();
        $this->assertEquals(0, $firstExample->getCallCount());
        $firstExample->do_stuff(false);
        $this->assertEquals(1, $firstExample->getCallCount());

        // Now test the other code path
        $secondExample = new TestableExampleClass();
        $this->assertEquals(0, $secondExample->getCallCount());
        $secondExample->do_stuff(true);
        $this->assertEquals(2, $secondExample->getCallCount());
    }
}

I wonder though whether counting the number of times a protected method has been called is really a good test. It's coupling your test to the implementation pretty hard. Does it really matter whether it is called twice, or are you more interested in the interactions with other objects? Or maybe this is pointing towards do_cool_stuff needing a refactor into two separate methods:

class ExampleClass {
    public function do_stuff($runTwice) {
       if ($runTwice) {
          return $this->do_cool_stuff_twice();
       } else {
          return $this->do_cool_stuff_once();
       }
    }
    //...
 }

Upvotes: 4

Related Questions