0

I have a doubt regarding the testing of private/protected methods. This is a general question that addresses unit testing for any platform. But just to tell you, I am working with phpunit a platform to unit test php.

Should we test private/protected methods? Accepted answer on this question says we normally should not. From that answer :

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 show not "break" your tests if you change it.

But at the same time other answers in so many questions provides a way to test them (I assume these answers mean we should test private/protected methods as they don't say that we should or should not test them).

Please explain me with reasons. Thank you.

Community
  • 1
  • 1
Geek
  • 8,280
  • 17
  • 73
  • 137
  • I've voted to close as primarily opinion-based. I think this will prompt too much debate without factual answers. – Duncan Jones Jul 30 '13 at 07:30
  • There are many questions (and answers) on SO on how to do the wrong thing... :) – Torbjörn Kalin Jul 30 '13 at 07:32
  • @DuncanJones Ok. Then I request you to suggest me what to do if I have this doubt. – Geek Jul 30 '13 at 07:33
  • @Akash There are many online forums that do not constrain the nature of the question being asked. The Stack Exchange sites have a deliberately narrow scope of acceptable questions, which I believe this falls outside. But it would be very welcome elsewhere I'm sure. – Duncan Jones Jul 30 '13 at 07:41

3 Answers3

1

As for me - it depends on complexity of private/protected members.

For example you have private methods Func1() and Func2() with complex computation or algorithms with many input parameters. And you have a public API that uses both them. And on some data Func1() is broken and returns incorrect data, but during processing in Func2() this data somehow converts to correct result (as a result of other issue). Total result of public API method will be correct - that means YOU HAVE INCORRECT Func1 and Func2 but your API returs correct somehow. And you testet it and it's fine.

Now you gave you code to somebody, and he/she created Func3() and one more public API method that uses Func1() and Func3() and unit test for this API method fails. How much time he/she will spend to find out that the reason is in Func1? And when Func1 will be fixed you will have previous test with Func2 failing... That's totaly not good.

So, IMHO you SHOULD test private/protected methods if they are complex (much code or usage of not obvious algo) and reusable. And of course you don't need to create 100 unit tests for private method that returns a + b or writing debug log record.

Hope my answer helps you! Best regards, Mikhail.

Mikhail Churbanov
  • 4,436
  • 1
  • 28
  • 36
1

Private and protected methods are implementation details of the code under test and should not be tested. Your tests tell you "what" your code is supposed to do. Testing private and protected methods starts getting into "how" your code is supposed to do what ever it is doing.

When you modify your class to add some private methods because you found some code reuse and you have tests failing you know that it is because you changed some functionality and need to fix that test case. If you have tests for private and protected methods and you do the same thing now you need to see if the tests are failing for a valid reason or are from your re-factoring. The private and protected methods are covered via the tests for the public interface of the class and do not need any tests of their own.

Just because you "can" do something doesn't mean that you "should" do something.

When I am testing, I consider the system under test to be a black box. I give it something and I expect something to happen. I do not care about what happens inside. All that is important is that the correct output is given.

Complicated internal functions are a code smell that perhaps I have my class doing too much and probably has another class hiding inside.

Schleis
  • 41,516
  • 7
  • 68
  • 87
0

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.

Steven Scott
  • 10,234
  • 9
  • 69
  • 117
  • Thank you for your answer. I understand why we don't need to test library. But can you elaborate why we should test private/protected methods of business application? – Geek Jul 31 '13 at 04:52
  • The business application still needs to work. Quite a number of application classes uses Private/Protected so outside classes can not change the values. However, if I am doing non trivial work in the class method (and it could be private), then I want to ensure that the class is tested and that method does not change. I have posted an pseudo example as an answer to help explain this. – Steven Scott Aug 01 '13 at 13:19