Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

I'm trying to advocate unit testing in my workgroup, but an objection I often get is that it should be used only for externally exported API (which is only a minimal and non critical part of our system), and not on internal and private code (which now has only functional testing).

While I think that unit test can and should be applied to all the code, how can I persuade my coworkers?

share|improve this question
2  
If you have private methods that you feel the need to test, that's often a sign that your code is violating the SRP & there's another class in there crying out to be extracted and tested in its own right. –  Paddyslacker Oct 11 '10 at 4:28
    
@Paddyslacker: I feel that all the code needs to be tested. I don't see why a code unit that follows single responsibility principle shouldn't be subjected to unit testing... –  Lorenzo Oct 11 '10 at 8:30
3  
@lorenzo, you missed my point; maybe I didn't make it very well. If you extract these private methods to another class they'll now need to be accessible from your original class. Because the methods are now public, they'll need to be tested. I wasn't implying that they shouldn't be tested, I was implying that if you feel the need to directly test the methods, it's likely that they shouldn't be private. –  Paddyslacker Oct 11 '10 at 14:42
    
@Paddyslacker: I feel the need to directly test also private methods. Why do you think that they shouldn't be private? –  Lorenzo Oct 11 '10 at 15:57
5  
By testing private methods you're breaking abstraction. You should be testing state and/or behaviour, not implementation, in unit tests. Your examples/scenarios should be able to verify what the result of the private code is up to - if you find that difficult, then as Paddyslacker says it could mean that you're violating SRP. It could also mean though that you haven't distilled your examples to be truly representative of what your code is doing. –  FinnNk Oct 11 '10 at 19:49

7 Answers 7

up vote 8 down vote accepted

Your coworkers may be confusing true unit tests with integration tests. If your product is (or has) an API, the integration tests can be programmed as NUnit test cases. Some people mistakenly believe that those are unit tests.

You can try to convince your coworkers with the following (I'm sure you already know this stuff, all I'm saying is that pointing it out to your co-workers might help):

  • Test coverage. Measure the actual test coverage percentage of those integration tests. This is a reality check for those who have never run test coverage. Because it is difficult to exercise all logical paths when the input is several layers away, the test coverage tops out at somewhere between 20% and 50%. To get more coverage, your co-workers need to write real, isolated unit-tests.
  • Configuration. Deploy the same software under test and perhaps you can demonstrate to your co-workers how difficult it is to run their tests in a different environment. Paths to various files, DB connection strings, URLs of remote services, etc. -- it all adds up.
  • Execution time. Unless the tests are true unit tests and can run in memory, they will take a lot of time to run.
share|improve this answer

The reasons for using unit tests on internal/private code are precisely the same as for externally supported APIs:

  • They prevent bugs from recurring (unit tests form part of your regression test suite).
  • They document (in an executable format!) that the code works.
  • They provide an executable definition of what "the code works" means.
  • They provide an automated means of demonstrating that the code does indeed match specs (as defined by the point above).
  • They show how the unit/class/module/function/method fails in the presence of unexpected input.
  • They provide examples on how to use the unit, which is great documentation for new team members.
share|improve this answer
    
+1 unit tests is the angel shoulder –  user2567 Oct 10 '10 at 12:55

here's another reason: in the hypothetical case I would have to choose between unit testing the external API vs the private parts, I'd choose the private parts.

If every private part is covered by a test, the API consisting of these private parts should be covered for almost 100% as well, excpt for just the upper layer. But that is likely to be a thin layer.

On the other hand when only testing the API it can be really hard to fully cover all possible code paths.

share|improve this answer
    
+1 "on the other hand..." But if nothing else, add tests where a failure would hurt the most. –  Tony Ennis Oct 10 '10 at 14:51

It's hard to get people to accept unit testing because it seems like a waste of time ("we could be coding another money-making project!") or recursive ("And then we have to write test cases for the test cases!") I am guilty of saying both.

The first time you find a bug, you have to face the truth that you're not perfect (how quickly we programmers forget!) and you go, "Hmmm."


Another aspect of unit testing is that the code must be written to be testable. Realizing that Some Code is easily testable and Some Code is not makes a good programmer go "Hmmm."


Did you ask your coworker why unit testing was only useful for external-facing APIs?


One way to show the value of unit testing is to wait for a nasty bug to happen and then show how unit testing could have prevented it. That's not to rub it in their faces, that's to, in their minds, move unit testing from an Ivory Tower Theoretical to an in-the-trenches reality.

Another way is to wait until the same error happens twice. "Uhhh, well Boss, we added code to test for a null after last week's problem, but the user entered an empty thing this time!"


Lead by example. Write unit tests for YOUR code, then show your boss the value. Then see if the boss will call in pizza for lunch one day and give a presentation.


Finally, I can't tell you the relief I feel when we're about to push to prod and I get a green bar from the unit tests.

share|improve this answer

If you mean private the way I think you mean it, then no - you shouldn't be unit testing it. You should only be unit testing observable behaviour/state. You may be missing the point behind the "red-green-refactor" cycle of TDD (and if you're not doing test first then the same principle apply). Once the tests are written and passing you do not want them to be changing whilst performing the refactoring. If you're forced to unit test private functionality then it probably means that the unit tests around the public functionality are flawed. If it's difficult and complex to write tests around the public code then maybe your class is doing too much or your problem isn't clearly defined.

Worse, over time your unit tests will become a ball and chain slowing you down without adding any value whatsoever (changing implementation, for example optimisation or removal of duplication, should have no effect on the unit tests). Internal code should, however, be unit tested as the behaviour/state is observable (just in a restricted way).

When I first did unit testing I pulled all sorts of tricks to unit test private stuff but now, with a few years under my belt, I see it as worse than a waste of time.

Here's a bit of a silly example, of course in real life you would have more tests than these:

Let's say you have a class that returns a sorted list of strings - you should check that the result is sorted, not how it actually sorts that list. You could start your implementation with a single algorithm that just sorts the list. Once that's done your test doesn't need to change if you then change your sorting algorithm. At this point you have a single test (assuming that the sorting is embedded in your class):

  1. Is my result sorted?

Now say you want two algorithms (perhaps one is more efficient in some circumstances but not others), then each algorith could (and generally, should) be provided by a different class and your class picks from them - you can check this is happening for your chosen scenarios using mocks, but your original test is still valid and as we are only verifying observable behaviour/state it doesn't need to change. You end up with 3 tests:

  1. Is my result sorted?
  2. Given a scenario (let's say the initial list is almost sorted to begin with) is a call made to the class that sorts strings using algorithm X?
  3. Given a scenario (the initial list is in a random order) is a call made to the class that sorts strings using algorithm Y?

The alternative would have been to begin testing private code inside your class - you don't gain anything from this - the above tests tell me everything that I need to know as far as unit testing is concerned. By adding private tests you're building yourself a straight jacket, how much more work would it be if you not only checked that the result was sorted but also how it's sorted?

Tests (of this type) should only change when behaviour changes, start writing tests against private code and that goes out the window.

share|improve this answer
1  
Perhaps there's a misunderstanding on the meaning of "private". In our system 99% of the code is "private", then we have a small API for automating/remote controlling one of the components of the system. I mean unit testing the code of all the other modules. –  Lorenzo Oct 12 '10 at 9:27

There are two kinds of private code: private code that gets called by public code (or private code that gets called by private code that gets called by public code (or ...)) and private code that does not eventually get called by public code.

The former already gets tested through the tests for the public code. The latter cannot be called at all and thus should be deleted, not tested.

Note that when you do TDD it is impossible for untested private code to exist.

share|improve this answer
    
In our system 99% of the code is of the third kind: private, not called by public code, and essential for the system (only a minimal part of our system has an external, public API). –  Lorenzo Oct 12 '10 at 9:36
1  
"Note that when you do TDD it is impossible for untested private code to exist." <-- delete a test case, without knowing that that the test's the only test to cover a particular branch. OK, that's more "presently untested" code, but it's easy enough to see a later trivial refactoring changing that code... only your test suite no longer covers it. –  Frank Shearar Oct 12 '10 at 12:15

Unit testing is all about testing units of your code. It is up to you to define what a unit is. Your coworkers define units as API elements.

Anyway, testing API should result in exercising private code as well. If you define code coverage as an indicator of unit testing progress, you will end up in testing all your code. If some part of code has not been reached, give your coworkers three choices:

  • define another test case for covering that part,
  • analyze code for justifying why it cannot be covered in the context of unit testing but should be covered in other situations,
  • remove dead code that has neither been covered not justified.
share|improve this answer
    
In our system the API is only a minimal part, that allow automation/remote control for a third party application. Testing only the API accounts for a 1% code coverage... –  Lorenzo Oct 12 '10 at 9:22

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.