Phpunit: Doing Assertions on Non-Public Variables

PHPUnit: Doing assertions on non-public variables

For testing properties, I'd make the same arguments I make then talking about testing private methods.

You usually don't want to do this.

It's about testing observable behavior.

If you rename all your properties or decide to store them into an array you should not need to adapt your tests at all. You want your tests to tell you that everything still works! When you need to change the tests to make sure everything still works you lose all the benefits as you also could make an error changing the tests.

So, all in all, you lose the value of you test suite!


Just testing the get/set combinations would be ok enough but usually not every setter should have a getter and just creating them for testing is not a nice thing ether.

Usually, you set some stuff and then tell the method to DO (behavior) something. Testing for that (that the class does what is should do) is the best option for testing and should make testing the properties superfluous.


If you really want to do that there is the setAccessible functionality in PHP reflections API but I can't make up an example where I find this desirable

Finding unused properties to catch bugs / issues like this one:

The PHP Mess Detector As a UnusedPrivateField Rule

class Something
{
private static $FOO = 2; // Unused
private $i = 5; // Unused
private $j = 6;
public function addOne()
{
return $this->j++;
}
}

This will generate two warnings for you because the variables are never accessed

Tell phpunit to call public instead of private

Short answer: No, there isn't. There is no way for PHPUnit to manipulate private methods.

Mocks are for faking the behavior of classes, which depend on the class you want to test. If you need to overwrite a private method, there is something wrong in your architecture. Private methods should be tested by simple testing the public methods in which they are used. If you need to test it separately, declare it public.

EDIT: Here is an example how you'd test a simple code using a private method or putting this private method in another class:

1. Private method

class MyClass {

public function myMethod() {
return $this->myPrivateMethod();
}

private function myPrivateMethod() {
return 2;
}

}

And the test class:

class MyClassTest extends \PHPUnit_Framework_TestCase {

public function testMyMethod() {
$myClass = new MyClass();
// don't mind the private method, just test if the public method is working
$this->assertEquals(2, $myClass->myMethod());
}

}

1. Second class

class MyClass {

public $dependentClass;

public function myMethod() {
return $this->dependentClass->dependentMethod();
}

}

And the dependent class:

class DependentClass {

public function dependentMethod() {
return 38943;
}

}

Testing MyClass with the DependentClass:

class MyClassTest extends \PHPUnit_Framework_TestCase {

public function testMyMethod() {
$dependentMock = $this->getMockBuilder("DependentClass")->getMock();
$dependentMock->expects($this->any())
->method("dependentMethod")
->willReturn($this->returnValue(2));

$myClass = new MyClass();
$myClass->dependentClass = $dependentMock;

$this->assertEquals(2, $myClass->myMethod());
}

Although we defined a whole other return in DependentClass, the test will still pass, because we mocked it and are able to define every behavior we'd expect DependentClass to have.

But to cleanly test the project, we need to define another test for DependentClass as well to test, if this class is working. This test should fail if it is not working anymore, not the test for MyClass.

Testing API keys on initialization

The code you show actually does not make sense. The class you want to test offers no way to the outside world to get these private properties back, so currently there is no use case for that class at all in production code.

But I suppose that class really contains at least one method that does make an API call to somewhere, using these two bits of information. You would test that the strings passed into the constructor show up in this call, because it is irrelevant whether or not they were stored in private properties, they must be present in the API call.

And to test this, you enter the world of mocking. There should be an object inside your api class that does the actual communication. And this object will get a method call with these secret strings. You should configure a mock object that checks that these strings are correctly passed.

If you do, you can then refactor anything related to storing these strings. You can rename the variables, you can move them into other object - anything would be possible, with your test still staying green because they do NOT test the internal structures of your class, only the observable behaviour from the outside. The outside world only cares about that.

But if you really thing you must test private properties: Use Reflection to make them accessible inside your test. Or write a getter method to read them.

Variable unexpectedly being changed in PHPUnit test

The problem you appear to be having is that objects are passed by reference by default, so when you update one, you update all of them.

Ways around this include (but may not be limited to):

  • Using clone (though this only does a shallow copy, so watch for that)
  • Serialising then deserialising the object into a new variable


Related Topics



Leave a reply



Submit