Should I test private methods or only public ones?

2018-12-31 06:13发布

I have read this post about how to test private methods. I usually do not test them, because I always thought it's faster to test only public methods that will be called from outside the object. Do you test private methods? Should I always test them?

27条回答
美炸的是我
2楼-- · 2018-12-31 07:03

Yes I do test private functions, because although they are tested by your public methods, it is nice in TDD (Test Driven Design) to test the smallest part of the application. But private functions are not accessible when you are in your test unit class. Here's what we do to test our private methods.

Why do we have private methods?

Private functions mainly exists in our class because we want to create readable code in our public methods. We do not want the user of this class to call these methods directly, but through our public methods. Also, we do not want change their behavior when extending the class (in case of protected), hence it's a private.

When we code, we use test-driven-design (TDD). This means that sometimes we stumble on a piece of functionality that is private and want to test. Private functions are not testable in phpUnit, because we cannot access them in the Test class (they are private).

We think here are 3 solutions:

1. You can test your privates through your public methods

Advantages

  • Straightforward unit testing (no 'hacks' needed)

Disadvantages

  • Programmer needs to understand the public method, while he only wants to test the private method
  • You are not testing the smallest testable part of the application

2. If the private is so important, then maybe it is a codesmell to create a new separate class for it

Advantages

  • You can refactor this to a new class, because if it is that important, other classes may need it too
  • The testable unit is now a public method, so testable

Disadvantages

  • You dont want to create a class if it is not needed, and only used by the class where the method is coming from
  • Potential performance loss because of added overhead

3. Change the access modifier to (final) protected

Advantages

  • You are testing the smallest testable part of the application. When using final protected, the function will not be overridable (just like a private)
  • No performance loss
  • No extra overhead

Disadvantages

  • You are changing a private access to protected, which means it's accessible by it's children
  • You still need a Mock class in your test class to use it

Example

class Detective {
  public function investigate() {}
  private function sleepWithSuspect($suspect) {}
}
Altered version:
class Detective {
  public function investigate() {}
  final protected function sleepWithSuspect($suspect) {}
}
In Test class:
class Mock_Detective extends Detective {

  public test_sleepWithSuspect($suspect) 
  {
    //this is now accessible, but still not overridable!
    $this->sleepWithSuspect($suspect);
  }
}

So our test unit can now call test_sleepWithSuspect to test our former private function.

查看更多
时光乱了年华
3楼-- · 2018-12-31 07:03

If your private method is not tested by calling your public methods then what is it doing? I'm talking private not protected or friend.

查看更多
永恒的永恒
4楼-- · 2018-12-31 07:07

If you don't test your private methods, how do you know they won't break?

查看更多
笑指拈花
5楼-- · 2018-12-31 07:08

I think it's best to just test the public interface of an object. From the point of view of the outside world, only the behavior of the public interface matters and this is what your unit tests should be directed towards.

Once you have some solid unit tests written for an object you do not want to have to go back and change those tests just because the implementation behind the interface changed. In this situation, you've ruined the consistency of your unit testing.

查看更多
听够珍惜
6楼-- · 2018-12-31 07:10

I kind of feel compelled to test private functions as I am following more and more one of our latest QA recommendation in our project:

No more than 10 in cyclomatic complexity per function.

Now the side effect of the enforcing of this policy is that many of my very large public functions get divided in many more focused, better named private function.
The public function still there (of course) but is essentially reduced to called all those private 'sub-functions'

That is actually cool, because the callstack is now much easier to read (instead of a bug within a large function, I have a bug in a sub-sub-function with the name of the previous functions in the callstack to help me to understand 'how I got there')

However, it now seem easier to unit-test directly those private functions, and leave the testing of the large public function to some kind of 'integration' test where a scenario needs to be addressed.

Just my 2 cents.

查看更多
谁念西风独自凉
7楼-- · 2018-12-31 07:10

If the private method is well defined (ie, it has a function that is testable and is not meant to change over time) then yes. I test everything that's testable where it makes sense.

For instance, an encryption library might hide the fact that it performs block encryption with a private method that encrypts only 8 bytes at a time. I would write a unit test for that - it's not meant to change, even though it's hidden, and if it does break (due to future performance enhancements, for instance) then I want to know that it's the private function that broke, not just that one of the public functions broke.

It speeds debugging later.

-Adam

查看更多
登录 后发表回答