I´m trying to test if a protected method is called in a public interface.
<?php
abstract class SomeClassAbstract
{
abstract public foo();
public function doStuff()
{
$this->_protectedMethod();
}
protected function _protectedMethod();
{
// implementation is irrelevant
}
}
<?php
class MyTest extends PHPUnit_Framework_TestCase
{
public function testCalled()
{
$mock = $this->getMockForAbstractClass('SomeClass');
$mock->expects($this->once())
->method('_protectedMethod');
$mock->doStuff();
}
}
I know it is called correctly, but PHPUnit says its never called.
The same happens when I test the other way, when a method is never called:
<?php
abstract class AnotherClassAbstract
{
abstract public foo();
public function doAnotherStuff()
{
$this->_loadCache();
}
protected function _loadCache();
{
// implementation is irrelevant
}
}
<?php
class MyTest extends PHPUnit_Framework_TestCase
{
public function testCalled()
{
$mock = $this->getMockForAbstractClass('AnotherClass');
$mock->expects($this->once())
->method('_loadCache');
$mock->doAnotherStuff();
}
}
The method is called but PHPUnit says that it is not.
What I´m doing wrong?
Edit I wasn´t declaring my methods with double colons, it was just for denoting that it was a public method (interface). Updated to full class/methods declarations.
Edit 2 I should have said that I´m testing some method implementations in an abstract class (edited the code to reflect this). Since I can not instantiate the class, how can I test this?
I´m thinking in creating an SomeClassSimple
extending SomeClassAbstract
and testing this one instead. Is it the right approach?
So yes, you would test private and protected methods if you felt they needed to be tested for you to answer Yes to the question. The take on (2) Preventing regression of behavior: Once you've got working code, you need to have a mechanism in place to protect this code from future damage.
The easiest way would be to make sure your tests are in the same package hierarchy as the class you are testing. If that's not possible then you can subclass the original class and create a public accessor that calls the protected method.
To test a protected method using junit and mockito, in the test class (the class used to test the method), create a “child class” that extends the protagonist class and merely overrides the protagonist method to make it public so as to give access to the method to the test class, and then write tests against this child ...
A protected member is accessible within its class and by derived class instances. In the unit test you can then use ExposedFooRequirementHandler as the class under test.
In the version of PHPUnit that I have, the class PHPUnit_Framework_MockObject_Mock
uses PHP's get_class_methods
to determine the interface of the object being mocked. get_class_methods
will pick out only the public methods, not the protected or private ones.
This is in keeping with the spirit of xUnit unit testing. Consider the PHPUnit docs' example on how to use Mock Objects. There, the SUT is Subject
, which has a protected method notify
. The method being tested, though, is doSomething
. Considering Subject
as a black box, we don't care about the details of how it is implemented. We do care, though, about its behavior. Specifically, we require that when we call doSomething
, then the update
method of any attached observers is called. So we mock the third party object Observer
, and we set up the expectation that its update
method will be called. Note that although the protected method notify
is exercised, it is not named explicitly in the test. That gives us the freedom to change the implementation any time we like, just so long as the behavior is preserved.
Returning to your example.
class MyTest extends PHPUnit_Framework_TestCase
{
public function testCalled()
{
$mock = $this->getMock('SomeClass');
$mock->expects($this->once())
->method('_protectedMethod');
$mock->doStuff();
}
}
Here, your testCalled
method creates no instance of the System Under Test (SUT), which would be SomeClass. The version of doStuff
in the mock of SomeClass doesn't do anything. In particular, it doesn't call _protectedMethod
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With