tags:

views:

71

answers:

3

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 the concept.

So,

  • What sorts of functions benefit from unit testing?
  • What sorts of functions shouldn't be unit tested?
A: 

According to TDD (Test Driven Development) methodology one should test every public function, and every path of execution in that function.

qertoip
@qertoip: but things like controllers and routers (i.e. front controllers) are public, and I don't see how unit tests can cover them. This is the problem I asked about in the question I linked to, and the raison d'être for this question. Should this be amended to something like "every public function that returns a value", or is there a way to make unit testing cover these instances?
AgentConundrum
Controllers and routers should be tested as well. It's not a theory, it is done in practice on a daily basis. Some frameworks, i.e. Rails, make it easier than others. Example tests for controllers: http://guides.rubyonrails.org/testing.html#functional-tests-for-your-controllers Example tests for routes: http://guides.rubyonrails.org/testing.html#testing-routes
qertoip
A: 

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.

Timo Westkämper
@Timo: I'm sorry, I'm getting a bit lost in your terms ("DAO signatures", "DTOs"). Could you clarify this a bit?
AgentConundrum
* DAO = http://en.wikipedia.org/wiki/Data_access_object (services for persistence abstraction)* DTO = http://en.wikipedia.org/wiki/Data_transfer_object (value objects)
Timo Westkämper
+1  A: 

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...

  1. 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.
  2. 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.
  3. 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.
  4. 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.
David
@David: You again! I can tell I'm going to learn a lot from you.. Two things: 1) I already understand #1/#4 just by reading a lot about it, and vague experience with similar concepts - it's the same reason as why QA departments are needed - a developer knows too much about the code and would retrofit the test to pass the code. 2) going back to the mvc question you also answered for me, how do you reconcile "unit testing public methods" with things like controllers and routers/front controllers? These objects have public interfaces, but I don't see how you can fit unit testing onto them.
AgentConundrum
@AgentConundrum: Me again? :) One of the big benefits of controllers in MVC over code-behind in WebForms is that they can easily be unit tested. Take a look here: http://www.lostechies.com/blogs/chrismissal/archive/2010/02/05/unit-testing-simple-asp-net-mvc-controllers.aspx and http://msdn.microsoft.com/en-us/magazine/dd942838.aspx and http://www.asp.net/mvc/tutorials/creating-unit-tests-for-asp-net-mvc-applications-vb The "context" of the application can be mocked so you just need to test that the actions return correct views. Mocks on dependencies verify that they were properly called.
David
(Sorry if I'm assuming .NET here, it's just been my experience. The concepts can be universally applied though, regardless of the tools used.)
David
@David: I was going with PHP, but with a bit of time I can translate your articles. If it makes you feel better, another comment linked to ruby info, and I don't even know where to begin applying that to PHP ;)
AgentConundrum
@AgentConundrum: This is a pretty good opportunity, actually. It goes back to really learning the concepts vs. just following a tutorial and not really learning anything. Just avoid trying to wedge the concepts of one language into another, but if you can apply the design concepts in a language/environment-agnostic manner then you're better off for it.
David
@David, I agree for the most part with what you have said, and I agree that it is better to write tests first. I disagree with your thought that unit testing for currently existing applications has little benefit. How else would one refactor?
Chance
@Chance: Well, it _often_ has little benefit. And it's certainly a lot more difficult than doing TDD in the first place. Usually what ends up happening is that tests are written against the existing code (usually by the same programmer(s) who wrote the code) just to validate that it does what it already does. If one is doing a lot of heavy re-factoring, to the point that one can consider the application as being essentially re-written in the process, then one certainly should write tests for that. But unit tests on legacy code, namely bad legacy code, is often more trouble then it's worth.
David