私有/受保护的方法是否应该进行单元测试?

在 TDD 开发中,您通常要做的第一件事情是创建接口,然后开始针对该接口编写单元测试。随着 TDD 过程的进行,您最终将创建一个实现接口的类,然后在某个时候您的单元测试将通过。

现在我的问题是,为了支持接口公开的方法/属性,我可能必须在类中编写私有和受保护的方法:

  • 类中的私有方法应该有自己的单元测试吗?

  • 类中受保护的方法是否应该有自己的单元测试?

我的想法是:

  • 特别是因为我正在编写接口代码,所以我不应该担心受保护/私有方法,因为它们是黑盒子。

  • 因为我使用的是接口,所以我编写单元测试来验证定义的契约是否由实现接口的不同类正确实现,所以我不必担心私有/受保护的方法,它们应该通过调用接口定义的方法/属性的单元测试来执行。

  • 如果我的代码覆盖率没有显示受保护/私有方法被访问,那么我就没有正确的单元测试,或者我的代码没有被使用,应该被删除。

62141 次浏览

No, I don't think of testing private or protected methods. The private and protected methods of a class aren't part of the public interface, so they don't expose public behavior. Generally these methods are created by refactorings you apply after you've made your test turn green.

So these private methods are tested implicitly by the tests that assert the behavior of your public interface.

On a more philosophical note, remember that you're testing behavior, not methods. So if you think of the set of things that the class under test can do, as long as you can test and assert that the class behaves as expected, whether there are private (and protected) methods that are used internally by the class to implement that behavior is irrelevant. Those methods are implementation details of the public behavior.

No, you shouldn't test private methods (how would you anyway without using something horrible like reflection). With protected methods it is slightly less obvious in C# you can make things protected internal and I think it is OK to do that to test derived classes that implement all of their functionality through template pattern methods.

But, in general, if you think that your public methods are doing too much then it is time to refactor your classes into more atomic classes and then test those clases.

No! Only test interfaces.

One of the big benefits of TDD is assuring that the interface works no matter how you've chosen to implement the private methods.

When you're writing the unit tests for your class, you shouldn't necessarily care whether or not the functionality of the class is implemented directly in the method on the public interface or if it is implemented in a series of private methods. So yes, you should be testing your private methods, but you shouldn't need to call them directly from your test code in order to do so (directly testing the private methods tightly couples your implementation to your tests and makes refactoring unnecessarily hard).

Protected methods form a different contract between your class and its future children, so you should really be testing it to a similar extent as your public interface to ensure that the contract is well defined and exercised.

I agree with everyone else: The answer to your question is 'no'.

Indeed you are entirely correct with your approach and your thoughts, especially about code coverage.

I would also add that the question (and the answer 'no') also applies to public methods that you might introduce to classes.

  • If you add methods (public/protected or private) because they make a failing test pass, then you've more or less achieved the goal of TDD.
  • If you add methods (public/protected or private) because you just decide to, violating TDD, then your code coverage should catch these and you should be able to improve your process.

Also, for C++ (and I should think only for C++) I implement interfaces using private methods only, to indicate that the class should only be used via the interface it implements. It stops me mistakenly calling new methods added to my implementation from my tests

You wrote:

In TDD development, the first thing you typically do is to create your interface and then begin writing your unit tests against that interface. As you progress through the TDD process you would end-up creating a class that implements the interface and then at some point your unit test would pass.

Please let me rephrase this in BDD language:

When describing why a class is valuable and how it behaves, the first thing you typically do is to create an example of how to use the class, often via its interface*. As you add desired behavior you end up creating a class which provides that value, and then at some point your example works.

*May be an actual Interface or simply the accessible API of the class, eg: Ruby doesn't have interfaces.

This is why you don't test private methods - because a test is an example of how to use the class, and you can't actually use them. Something you can do if you want to is delegate the responsibilities in the private methods to a collaborating class, then mock / stub that helper.

With protected methods, you're saying that a class which extends your class should have some particular behavior and provide some value. You could then use extensions of your class to demonstrate that behavior. For instance, if you were writing an ordered collection class, you might want to demonstrate that two extensions with the same contents demonstrated equality.

Hope this helps!

Completing what others said above, I would say that protected methods are part of an interface of some kind: it simply happens to be the one exposed to inheritance instead of composition, which is what everyone tends to think about when considering interfaces.

Marking a method as protected instead of private means it is expected to be used by third party code, so some sort of contract needs to be defined and tested, as happens with normal interfaces defined by public methods, which are open both for inheritance and composition.

I disagree with most of the posters.

The most important rule is: WORKING CODE TRUMPS THEORETICAL RULES about public/protected/private.

Your code should be thoroughly tested. If you can get there by writing tests for the public methods, that sufficiently exercise the protected/private methods, that's great.

If you can't, then either refactor so that you can, or bend the protected/private rules.

There's a great story about a psychologist who gave children a test. He gave each child two wooden boards with a rope attached to each end, and asked them to cross a room w/o touching their feet to the floor, as fast as possible. All the kids used the boards like little skis, one foot on each board, holding them by the ropes, and sliding across the floor. Then he gave them the same task, but using only ONE board. They pivoted/"walked" across the floor, one foot on each end of the single board -- and they were FASTER!

Just because Java (or whatever language) has a feature (private/protected/public) does not necessarily mean you are writing better code because you use it!

Now, there will always be ways to optimize/minimize this conflict. In most languages, you can make a method protected (instead of public), and put the test class in the same package (or whatever), and the method will be available for test. There are annotations that can help, as described by other posters. You can use reflection to get at the private methods (yuck).

The context also matters. If you're writing an API for use by external people, public/private is more important. If it's an internal project -- who really cares?

But at the end of the day, think about how many bugs have been caused by lack of testing. Then compare to how many bugs have been caused by "overly visible" methods. That answer should drive your decision.

I too agree with @kwbeam's answer about not testing private methods. However, an important point I'd like to highlight - protected methods ARE part of a class' exported API and hence MUST be tested.

Protected methods may not be publicly accessible but you definitely are providing a way for sub-classes to use/override them. Something outside the class can access them and hence you need to ensure that those protected members behave in an expected manner. So don't test private methods, but do test public and protected methods.

If you believe you have a private method which contains critical logic, I'd try to extract it out into a separate object, isolate it and provide a way to test its behavior.

Hope it helps!

There's two reasons for writing tests:

  1. Asserting expected behavior
  2. Preventing regression of behavior

The take on (1) Asserting expected behavior:

When you're asserting expected behavior, you want to make sure the code works as you think it should. This is effectively an automated way of doing your routine manual verification that any dev would perform when implementing any kind of code:

  • Did what I just write works?
  • Does this loop actually end?
  • Is it looping in the order I think it is?
  • Would this work for a null input?

Those are questions we all answer in our heads, and normally, we'd try to execute the code in our heads too, make sure it looks like it does work. For these cases, it is often useful to have the computer answer them in a definitive manner. So we write a unit test that assert it does. This gives us confidence in our code, helps us find defects early, and can even help actually implementing the code.

It's a good idea to do this wherever you feel is necessary. Any code that is a little tricky to understand, or is non trivial. Even trivial code could benefit from it. It's all about your own confidence. How often to do it and how far to go will depend on your own satisfaction. Stop when you can confidently answer Yes to: Are you sure this works?

For this kind of testing, you don't care about visibility, interfaces, or any of that, you only care about having working code. 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. If nobody was to ever touch your source and your config ever again, you wouldn't need this, but in most cases, you or others will be touching the source and the configs of your software. This internal fiddling is highly likely to break your working code.

Mechanisms exist in most languages already as a way to protect against this damage. The visibility features are one mechanism. A private method is isolated, and hidden. Encapsulation is another mechanism, where you compartmentalize things, so that changing other compartments doesn't affect others.

The general mechanism for this is called: coding to boundary. By creating boundaries between parts of the code, you protect everything inside a boundary from things outside of it. The boundaries become the point of interaction, and the contract by which things interact.

This means that changes to a boundary, either by breaking it's interface, or breaking it's expected behavior, would damage and possibly break other boundaries which relied on it. That's why it's a good idea to have a unit test, that targets those boundaries and assert they don't change in semantic and in behavior.

This is your typical unit test, the one most everybody talks about when mentioning TDD or BDD. The point is to hardened the boundaries and protect them from change. You do not want to test private methods for this, because a private method is not a boundary. Protected methods are a restricted-boundary, and I would protect them. They aren't exposed to the world, but are still exposed to other compartments or "Units".

What to make of this?

As we've seen, there's a good reason to unit test public and protected methods, as to assert our interfaces don't change. And there's also good reason to test private methods, as to assert our implementation works. So should we unit test them all?

Yes and No.

Firstly: Test all methods that you feel you need a definitive proof that it works in most cases as to be able to be confident your code works, no matter the visibility. Then, disable those tests. They've done there job.

Lastly: Write tests for your boundaries. Have a unit test for each point that will be used by other units of your system. Make sure this test assert the semantic contract, method name, number of arguments, etc. And also make sure the test asserts the available behavior of the unit. Your test should demonstrate how to use the unit, and what the unit can do. Keep these tests enabled so that they run on every code push.

NOTE: The reason you disabled the first set of test is to allow refactoring work to occur. An active test is a code coupling. It prevents future modification of the code it's testing. You only want this for your interfaces and interaction contracts.

If you are aiming high code coverage (I suggest you should), you should test your all methods regardless of they are private or protected.

Protected is a kind of different discussion point, but in summary, it should not be there at all. Either it breaks encapsulation on deployed code, or it forces you to inherit from that class, just to unit test it, even sometimes you do not need to inherit.

Just hiding a method to client (making private) does not give it to have privilege not to be audited. Therefore, they can be tested by public methods as mentioned before.

A good design means splitting the application into multiple testable units. After doing this, some units are exposed to the public API, but some others may not be. Also, the interaction points between exposed units and these "internal" units are also not a part of the pubic API.

I think once we have the identifiable unit, is would benefit from the unit tests, regardless if exposed via public API or not.

Simple answer - NO.

Explanation : why should test a private function ? It will automatically be tested anyway (and must be tested) when you test the feature/method which is using it - the private function.