Unit testing private code [duplicate]

2019-02-02 09:57发布

This question already has an answer here:

I am currently involved in developing with C# - Here is some background: We implement MVP with our client application and we have a cyclomatic rule which states that no method should have a cyclomatic complexity greater than 5. This leads to a lot of small private methods which are generally responsible for one thing.

My question is about unit testing a class:

Testing the private implementation through the public methods is all fine... I don't have a problem implementing this.

But... what about the following cases:

Example 1. Handle the result of an async data retrival request (The callback method shouldn't be public purely for testing)

Example 2. An event handler which does an operation (such as update a View label's text - silly example I know...)

Example 3. You are using a third party framework which allows you to extend by overriding protected virtual methods (the path from the public methods to these virtual methods are generally treated as black box programming and will have all sorts of dependancies that the framework provides that you don't want to know about)

The examples above don't appear to me to be the result of poor design. They also do not appear be be candidates for moving to a seperate class for testing in isolation as such methods will lose their context.

Doesn anyone have any thoughts about this?

Cheers, Jason

EDIT: I don't think I was clear enough in my original question - I can test private methods using accessors and mock out calls/ methods using TypeMock. That isn't the problem. The problem is testing things which don't need to be public, or can't be public.

I don't want to make code public for the sake of testing as it can introduce security loopholes (only publishing an interface to hide this is not an option because anyone can just cast the object back to its original type and get access to stuff I wouldn't want them to)

Code that gets refactored out to another class for testing is fine - but can lose context. I've always thought it bad practice to have 'helper' classes which can contain a pot of code with no specific context - (thinking SRP here). I really don't think this works for event handlers either.

I am happy to be proven wrong - I just am unsure how to test this functionality! I have always been of the mind that if it can break or be changed - test it.

Cheers, Jason

12条回答
成全新的幸福
2楼-- · 2019-02-02 10:20

There is really only two cases you need to consider:

  1. the private code is called, directly or indirectly from public code and
  2. the private code is not called from public code.

In the first case, the private code is automatically being tested by the tests which exercise the public code that calls the private code, so there is no need to test the private code. And in the second case, the private code cannot be called at all, therefore it should be deleted, not tested.

Ergo: there is no need to explicitly test the private code.

Note that when you do TDD it is impossible for untested private code to even exist. Because when you do TDD, the only way that private code can be appear, is by an Extract {Method|Class|...} Refactoring from public code. And Refactorings are, by definition, behavior-preserving and therefore test-coverage-preserving. And the only way that public code can appear is as the result of a failing test. If public code can only appear as already tested code as the result of a failing test, and private code can only appear as the result of being extracted from public code via a behavior-preserving refactoring, it follows that untested private code can never appear.

查看更多
beautiful°
3楼-- · 2019-02-02 10:22

Would accessor files work? http://msdn.microsoft.com/en-us/library/bb514191.aspx I've never directly worked with them, but I know a coworker used them to test private methods on some Windows Forms.

查看更多
够拽才男人
4楼-- · 2019-02-02 10:22

Don't test private code, or you'll be sorry later when it's time to refactor. Then, you'll do like Joel and blog about how TDD is too much work because you constantly have to refactor your tests with your code.

There are techniques (mocks, stub) to do proper black box testing. Look them up.

查看更多
迷人小祖宗
5楼-- · 2019-02-02 10:30

A few points from a TDD guy who has been banging around in C#:

1) If you program to interfaces then any method of a class that is not in the interface is effectively private. You might find this a better way to promote testability and a better way to use interfaces as well. Test these as public members.

2) Those small helper methods may more properly belong to some other class. Look for feature envy. What may not be reasonable as a private member of the original class (in which you found it) may be a reasonable public method of the class it envies. Test these in the new class as public members.

3) If you examine a number of small private methods, you might find that they have cohesion. They may represent a smaller class of interest separate from the original class. If so, that class can have all public methods, but be either held as a private member of the original class or perhaps created and destroyed in functions. Test these in the new class as public members.

4) You can derive a "Testable" class from the original, in which it is a trivial task to create a new public method that does nothing but call the old, private method. The testable class is part of the test framework, and not part of the production code, so it is cool for it to have special access. Test it in the test framework as if it were public.

All of these make it pretty trivial to have tests on the methods that are currently private helper methods, without messing up the way intellisense works.

查看更多
Juvenile、少年°
6楼-- · 2019-02-02 10:33

I will admit that when recently writing units tests for C# I discovered that many of the tricks I knew for Java did not really apply (in my case it was testing internal classes).

For example 1, if you can fake/mock the data retrieval handler you can get access to the callback through the fake. (Most other languages I know that use callbacks also tend not to make them private).

For example 2 I would look into firing the event to test the handler.

Example 3 is an example of the Template Pattern which does exist in other languages. I have seen two ways to do this:

  1. Test the entire class anyway (or at least relevant pieces of it). This particularly works in cases where the abstract base class comes with its own tests, or the overall class is not too complex. In Java I might do this if I were writing an extension of AbstractList, for example. This may also be the case if the template pattern was generated by refactoring.

  2. Extend the class again with extra hooks that allow calling the protected methods directly.

查看更多
我命由我不由天
7楼-- · 2019-02-02 10:36

This is a question that comes up pretty early when introducing testing. The best technique to solving this problem is to black-box test (as mentioned above) and follow the single responsibility principle. If each of your classes only have only one reason to change, they should be pretty easy to test their behavior without getting at their private methods.

SRP - wikipedia / pdf

This also leads to more robust and adaptable code as the single responsibility principle is really just saying that your class should have high cohesion.

查看更多
登录 后发表回答