Mock Private Method With PHPunit

Mock private method with PHPUnit

Usually you just don't test or mock the private & protected methods directy.

What you want to test is the public API of your class. Everything else is an implementation detail for your class and should not "break" your tests if you change it.

That also helps you when you notice that you "can't get 100% code coverage" because you might have code in your class that you can't execute by calling the public API.


You usually don't want to do this

But if your class looks like this:

class a {

public function b() {
return 5 + $this->c();
}

private function c() {
return mt_rand(1,3);
}
}

i can see the need to want to mock out c() since the "random" function is global state and you can't test that.

The "clean?/verbose?/overcomplicated-maybe?/i-like-it-usually" Solution

class a {

public function __construct(RandomGenerator $foo) {
$this->foo = $foo;
}

public function b() {
return 5 + $this->c();
}

private function c() {
return $this->foo->rand(1,3);
}
}

now there is no more need to mock "c()" out since it does not contain any globals and you can test nicely.


If you don't want to do or can't remove the global state from your private function (bad thing bad reality or you definition of bad might be different) that you can test against the mock.

// maybe set the function protected for this to work
$testMe = $this->getMock("a", array("c"));
$testMe->expects($this->once())->method("c")->will($this->returnValue(123123));

and run your tests against this mock since the only function you take out/mock is "c()".


To quote the "Pragmatic Unit Testing" book:

"In general, you don't want to break any encapsulation for the sake of testing (or as Mom used to say, "don't expose your privates!"). Most of the time, you should be able to test a class by exercising its public methods. If there is significant functionality that is hidden behind private or protected access, that might be a warning sign that there's another class in there struggling to get out."


Some more: Why you don't want test private methods.

PHPUnit testing a protected method that calls a private method which needs to be mocked

$result = $myProtectedMethod->invoke($reflectionClass, $mockArgOne, $mockArgTwo);

should be

$result = $myProtectedMethod->invoke($mmyMockClass, $mockArgOne, $mockArgTwo);

For more information how to use "invoke" method here. https://www.php.net/manual/en/reflectionmethod.invoke.php

Phpunit mock private function

There are two approaches to this problem:

  1. You unit test the private method as part of the call to the public method. Your goal when unit testing is to test the class's public API. You test the private method through the public API, so should be testing the return value including the value returned from the private method. If the private method relies on some external state, then you make sure that state is set up (and torn down) at the beginning and end of the test, respectively.
  2. Sometimes, the private method is question simply isn't testable directly. As an example, I recently had an instance where the method I wrote was reading from stdin. As far as I'm aware, you can't assign a value to stdin in your tests, so I had to split off the read to stdin into a separate method. Then, I created a test stub, which overrode the parent method to return a pre-defined value. This gives you control over the private method that you wouldn't have normally.

How to add a private property in mocked class with PHPUnit

Here is a solution based on xmike's comment and with the Phpunit doc here : https://phpunit.readthedocs.io/en/9.0/fixtures.html.

make a fixture class like this :

class GetSettingsFixture
{

public array $one = [
"oneOne" => "1.1",
"oneTwo" => "1.2",
"oneThree" => [
"oneThreeOne" => "1.3.1",
"oneThreeTwo" => "1.3.2",
]
];

public array $two = [
"twoOne" => "2.1",
"twoTwo" => "2.2",
"twoThree" => [
"twoThreeOne" => "1.3.1",
"twoThreeTwo" => "1.3.2",
]
];

public array $three = [
"threeOne" => "3.1",
"threeTwo" => "3.2"
];

public string $four = "a string";

private array $five = [ // this should be ignored in the output.
"fiveOne" => "5.1",
"fiveTwo" => "5.2"
];

protected array $six = [ // this should be ignored in the output.
"sixOne" => "6.1",
"sixTwo" => "6.2"
];

public function testFunction() { // this should be ignored in the output.
return "something";
}
}

And this test pass :

class GetSettingsTest extends TestCase
{
private GetSettingsFixture $given;

public function setUp(): void {
// this function is executed before test.
$this->given = new GetSettingsFixture(); // this call the fixture class.
}

public function tearDown(): void {
// this function is executed after the test.
unset($this->given);
}

public function getExpected() {
return (object) [
"one" => (object) [
"oneOne" => "1.1",
"oneTwo" => "1.2",
"oneThree" => (object) [
"oneThreeOne" => "1.3.1",
"oneThreeTwo" => "1.3.2",
]
],
"two" => (object) [
"twoOne" => "2.1",
"twoTwo" => "2.2",
"twoThree" => (object) [
"twoThreeOne" => "1.3.1",
"twoThreeTwo" => "1.3.2",
]
],
"three" => (object) [
"threeOne" => "3.1",
"threeTwo" => "3.2"
],
"four" => "a string"
// five, six are not here : it is protected or private.
// testFunction is hot here too, it's not a property.
];
}

public function testGetSettings() {
$expected = $this->getExpected();
$getSettings = new GetSettings($this->given);
$value = $getSettings->getSettings();
$this->assertEquals($expected, $value);
}

}

Best practices to test protected methods with PHPUnit

If you're using PHP5 (>= 5.3.2) with PHPUnit, you can test your private and protected methods by using reflection to set them to be public prior to running your tests:

protected static function getMethod($name) {
$class = new ReflectionClass('MyClass');
$method = $class->getMethod($name);
$method->setAccessible(true);
return $method;
}

public function testFoo() {
$foo = self::getMethod('foo');
$obj = new MyClass();
$foo->invokeArgs($obj, array(...));
...
}

Should you cover private methods with a separate unit test

The idea is that your class exposes a an interface and your tests verify the input/output is correct from that interface. Each class/method should have a single responsibility.

The technique you described of marking a method as protected virtual is one that I have used myself on occasion, but this was within the context of a large legacy code application where it was impractical to refactor everything into a testable state right away. This technique should be used sparingly.

Generally if you feel that a private method is complex enough that it needs its own tests you should probably extract it out to its own class. If the private method does not have a lot of complexity then you will be needlessly asserting implementation details and making your code hard to refactor in future.

If you need to set up many mock objects to test a single class then it is probably violation SRP. One of the great side affects of writing tests is that it highlights any poorly designed classes.

I highly recommend this book to you: https://www.artofunittesting.com/



Related Topics



Leave a reply



Submit