I believe the testing of the Private/Protected methods depends on where/how they are accessed, and what is being tested.
If I am building a reusable library of common code, then the private and protected methods within these classes are tested within the library test cases. These tests ensure that the lower level code works so I can have faith in the library.
Now, when I write a business application that accesses this library, I do not write tests for the library objects, as they will be mocked in the tests for the business application. The library tests shows that the library works, and then the business application tests show that the application works.
The business application does not try to test the private/protected methods of the library, as I consider that a black box of code that I do not know about (similar to an external library or web service). I do however test the private and protected methods of the business application in the test suite for the business application, again, to ensure that methods/functions are behaving as they should.
As an example then, assume the following Business Classes (very brief to convey thought, not functions):
<?php
class ACCOUNTS
{
protected GetEstimation()
{
...
return $CalculatedValue;
}
}
class CUSTOMER_ACCOUNT extends ACCOUNTS
{
protected GetEstimation()
{
$BaseEstimation = parent::GetEstimation();
...
return $NewCalculatedValue
}
}
class RESELLER_ACCOUNT extends ACCOUNTS
{
protected GetEstimation()
{
...
return $ResellerCalculatedValue; // Note: No call to parent
}
}
?>
In this example there are different values that are going to be returned. The Protected function was used so it could be overridden, and it does not have to rely on the parent class functionality. In this example, I do want to test all these classes return the proper values when they are used.
The basic ACCOUNTS class should return the Estimation after doing its calculations, based on the class values. As a rule, I simply set the values and test the returns:
<?php
class ACCOUNTSTest extends PHPUnit_Framework_TestCase
{
protected $Accounts;
protected function setUp()
{
$this->Accounts = new ACCOUNTS();
}
public function testEstimationHome()
{
$this->Accounts->InternalValue1 = 1;
$this->Accounts->InternalValue2 = 10;
$this->Accounts->InternalValue3 = 100;
$this->assertEquals(523, $this->Accounts->GetEstimation(), 'Test Home Account with values 1, 10, 1000');
}
public function testEstimationHome2()
{
$this->Accounts->InternalValue1 = 5;
$this->Accounts->InternalValue2 = 2;
$this->Accounts->InternalValue3 = 10;
$this->assertEquals(253, $this->Accounts->GetEstimation(), 'Test Home Account with values 5, 2, 10');
}
protected function tearDown()
{
unset($this->Accounts);
}
}
?>
These tests will now ensure that the ACCOUNTS->GetEstimation() is working correctly. I would then test CUSTOMER_ACCOUNT, and have similar tests to ensure that the class is calculating correctly.
Yes, if the base class changes, then I might need to update the tests in the CUSTOMER_ACCOUNT since the ACCOUNTS->GetEstimation() changed, but I also have an extra check that the base class is still returning correctly.
Alternately, I could change this structure and use Dependency Injection to provide certain information (ACCOUNTS) to ensure that if the parent estimation was always 523, then this class returns the proper values. If ACCOUNTS changes the estimate returned, and it does not matter to this class (maybe this class is simply adding additional values), then I isolate my test and do not need to worry.
Hopefully this update helps to illustrate what I am saying.