What should, and shouldn't, be covered by unit tests?
Clearly, I don't understand unit testing. This is fine, considering I've never done it before. I'm starting a new project, and wanted to bake unit testing into it from the beginning, so I'm looking to learn.
I had always equated unit testing with code coverage, thinking that you should have unit tests which cover every function/method in your application, but clearly this isn't the case and I've completely misunderstood t开发者_如何转开发he concept.
So,
- What sorts of functions benefit from unit testing?
- What sorts of functions shouldn't be unit tested?
I don't have a complete answer (I'd love to hear anybody who does, honestly), but can at least toss out a few points...
- You're already on the right track by driving the development from tests in the first place. Retro-fitting unit tests into existing applications is difficult and rarely provides real benefit (instead often gives a false sense of code coverage). Proper TDD is always a forethought, never an afterthought.
- I wouldn't bother testing private functions. Various code coverage tools may say it's left untested, but if it's private then its functionality should be tested by testing the public methods. Private methods are internal and not part of the API, nothing outside of the class (even the tests) should know or care about them, as they can easily change if the implementation of that class is changed.
- Focus on the exposed API of your code. Write tests against your interfaces, not against your classes. The classes themselves are later implemented and tested against the tests.
- Finally, and very importantly, study what you're doing and what its benefits are. Writing unit tests isn't a brown-and-serve process. One doesn't achieve good test coverage by simply writing tests, but by understanding TDD and how to implement it. It'll take practice. One suggestion I would give is to do a proper TDD project and also try to retro-fit tests into an existing project. We can tell each other all day that the former is better than the latter, but by doing both you can actually discern the differences and get a better understanding of why it's better. This will bring you beyond just writing the tests and into being more of an expert of TDD and what it really brings to the table. Anybody can write tests, but all too often they're just wasting their time unless they really understand what's going on.
According to TDD (Test Driven Development) methodology one should test every public function, and every path of execution in that function.
I had always equated unit testing with code coverage
That is not entirely wrong. Unit tests / any other sort of test automation have only one truly undeniable benefit that holds in 100% of cases: they actually run the code i.e. they cover it. Compiler doesn't run the code. Static code analysis doesn't run the code. Obviously reading the code won't make it run. Automated tests do run the code (as well as manual debugging).
Any other claimed benefits are only potential and fully depend on competence and best intentions of those who write tests.
thinking that you should have unit tests which cover every function/method in your application, but clearly this isn't the case
Correct, not every single line of code is worthwhile to be automated to run during build.
So,
- What sorts of functions benefit from unit testing?
- What sorts of functions shouldn't be unit tested?
With above in mind, at least two conditions should hold:
possibility of error can't be eliminated statically (try to design code the way that makes invalid states and scenarios impossible to represent and save yourself from heaps of unit tests)
possibility of error is not low, e.g. code is not trivial / very cohesive etc.
OR
this is a mission-critical component / deploying an error has dire consequences. In this case it's worth to be a paranoid, even if possibility of error is extremely low.
OR
Code is a part of published / externally visible contract e.g. a library interface. In that case even primitive tautological tests are useful as they act as executable requirements / protect you against unintentional changes of published contracts (sort of specific case of a previous bucket of mission-critical tests).
OR
Writing a test is actually easier than reaching this code manually, can be well true in large monolithic behemoths. It doesn't mean you have to keep the test around forever, feel free to delete it later if it incurs large maintenance costs. But code must be run and results must be observed and accepted at least once before deployment, of course.
Above is true as of my best current knowledge provided that one's priority is to be pragmatic. If priority is to have good excuses for sloppy job then aim for 100% coverage and other religious test practices are perfectly justified (think "it's not my fault - all tests were green!")
Unit testing is a tool, and there many approaches on how to use it. Here is my approach :
I write my unit tests against Service and DAO signatures, and not so much against DTOs and entity types. Most of the value types will be indirectly tested. equals and hashCode methods in DTOs and entity types should be tested though.
I use both pure unit tests with mocked dependencies and integration tests with the full backend. For proper coverage, one needs both.
精彩评论