0

Writing unit tests for code which is already written is fun sometimes.

I am writing a test case for the following code (an example):

<?php

class mockPrivate {

    public static function one($a){
        $var = static::_two($a);
        return $var;
    }

    private static function _two($a){
        return $a+1;
    }
}
?>

The test class is like this:

<?php

require_once 'mockPvt.php';

class mockPrivate_test extends PHPUnit_Framework_TestCase {
    public $classMock;
    protected function setUp(){ 
         $this->classMock = $this->getMock('mockPrivate', array('_two'));
    }

    public function test_one(){
        $a = 1;
        $retVal = 2;
        $classmock = $this->classMock;
        $classmock::staticExpects($this->once())
            ->method('_two')
            ->with($a)
            ->will($this->returnValue($retVal));
        $value = $classmock::one($a);
        $this->assertEquals($value, $retVal);                
    }    
}

?>

After running by $ phpunit mockPrivate_test.php I got this error:

PHP Fatal error:  Call to private method Mock_mockPrivate_531a1619::_two() from context 'mockPrivate' in /data/www/dev-sumit/tests/example
s/mockPvt.php on line 6

But if I change the

private    static function _two() 
to 
public     static function _two() or 
protected  static function _two() 

it works totally fine. Since this is a legacy code I can't change the private to public/protected. So is there any way I can test the function one or Is this a limitation of phpunit?

Sumitk
  • 1,485
  • 6
  • 19
  • 31
  • how about [is_callable()](http://php.net/manual/en/function.is-callable.php)? I genuinely don't know whether this function is context sensitive or not, it's not documented as far as I can see... – DaveRandom Aug 19 '11 at 22:42
  • 2
    possible duplicate of [PhpUnit private method testing](http://stackoverflow.com/questions/5937845/phpunit-private-method-testing) – Gordon Aug 20 '11 at 12:38

3 Answers3

2

Another option is to create a class that extends mockPrivate, allowing accessibility to the object you wish to test. Your engineers should be thinking long and hard about why something is private (because that means the class is not easily extensible). Also remember that you can mock the test class if you need to override what it returns.

class Test_MockPrivate extends MockPrivate
{
    /**
     * Allow public access to normally protected function
     */
    public static function _two($a){
        return parent::_two($a);
    }
}

// Code to force the return value of a now public function
$mock = $this->getMock('Test_MockPrivate', array('_two'));
$mock->expects($this->any())
    ->method('_two')
    ->will($this->returnValue('Some Overridden Value');
Mike Graf
  • 5,077
  • 4
  • 45
  • 58
  • Thanks Mike. I haven't got time to test this but was wondering, can we mock private functions with the new PHPUnit 3.6? – Sumitk May 07 '12 at 20:56
  • 1
    @Sumitk Looks like they cannot mock privates in 3.7 see: http://www.phpunit.de/manual/3.7/en/test-doubles.html "Limitations Please note that final, private and static methods cannot be stubbed or mocked. They are ignored by PHPUnit's test double functionality and retain their original behavior." – Mike Graf May 07 '12 at 22:38
  • Don't forget, protected methods cannot be mocked either. – alairock Feb 08 '14 at 01:29
1

You can use reflection for changing visibility of methods. You can find more info in PHP object, how to reference?

Community
  • 1
  • 1
Andrej
  • 7,474
  • 1
  • 19
  • 21
  • 2
    Reflection's `setAccessible()` will only allow you to call the method from outside the class via the reflection object. It won't change the fact that the method cannot be overridden which is required for mocking. – David Harkness Aug 21 '11 at 01:09
  • @David - I think the private cannot be mocked as it cant be overridden in this case. So does everyone who uses phpunit leave that function part uncovered where private functions are called? I did find this link - [link](http://sebastian-bergmann.de/archives/881-Testing-Your-Privates.html) where people have modified the framework to support testing private. I will try that. Thanks for your comment! – Sumitk Aug 25 '11 at 04:47
  • @Sumitk - You can use `setAccessible()` to *test* private methods (though many will say that's not a good idea), but for mocking you are out of luck. You can change it to protected or leave it unmocked when testing methods that call it. – David Harkness Aug 25 '11 at 17:17
0

Use mock and reflection... (posted this solution, since this is the top google result)

$oMock = $this->getMock("Your_class", array('methodToOverride'));
$oMock->expects( $this->any() )
    ->method('methodToOverride')
    ->will( $this->returnValue( true ) );

$oReflection = new ReflectionClass("Your_Class");

$oMethod = $oReflection->getMethod('privateMethodToInvoke');
$oMethod->setAccessible( true );
$oMethod->invoke( $oMock );