When you shouldn't bother unit testing
invalid-arg.github.io
invalid-arg.github.io
As well as the simple fact that your code will be more reliable, it forces you to write your code in a way that is testable, which in turn will make it loosely coupled and modular.
One of the major drawbacks of many current unit testing tools is that in some contexts they can have exactly the opposite effect. If you expose implementation details through your modules’ interfaces that would otherwise be hidden, just in order to support your unit tests, then that makes your code less modular and potentially more tightly coupled.
It could be a direct exposure, for example by adding public methods to a class that allow tests to access otherwise private fields in languages like Java or C++. It could also be indirect exposure, for example using a dependency injection scheme to initialize everything so you can substitute test dummies for live code, but then using something at the top of the tree that essentially has to know everything about everything in your application in order to set up all the injected parts.
These things happen to a greater or lesser extent depending on many factors: which tools you have, how dynamic your language is, and more. However, I would challenge any claim that using something like TDD necessarily implies that your code will evolve a robust, modular design. Extensive unit testing can promote modularity in some ways, but it’s definitely a two-way street in most contexts today.
I feel a bit funny about this, though; it does make the tests explicitly dependent on stuff that, to the rest of the code, is a hidden implementation detail. So, it's probably better to test only the public interface wherever that's feasible...
Obviously there is not "necessity" here. You can indeed add more coupling with wrong tests. But this is a side effect of some excessive cargo cult about TDD and you should not throw the tool away because some overuse it.
The claim that writing tests helps you define and describe you'r code interface still hold, and strongly.
I usually code in Python and run pylint on my prose. When it tells me I shouldn't call a private method or attribute from outside, it is for me a warning sign:
- I may not be testing the proper behavior.
- If I feel I need to test it inside, this code nugget has too much responsibilities.
- It may be the case that this private thing is really public (I am a private-by-default coder), but this is rare.
When you don't have the time to refactor it properly, I admit that in some cases there are only bad choices: writing a long unatomic scenario testing, testing atomically inners, or not testing anything at all. The later is the easy path for short-sighted development, but adding some testing, even subpar, is much better, because at least it shows Someone Once New How To Roughly Run this part of the code base.
That said, writing good unit tests, doing TDD correctly, etc. are all skills that require experience. Just using TDD is no more likely to guarantee good code than any other software development process. But no process at all is almost always worse.
I hardly test private methods for this very reason. The methods are private and are meant to be helpers. You can poke at it through the method that implements it but I tend to stay away from testing private methods.
To head off the 'only test your public method camp':
I often have bits of math or 'tricky stuff' in a private method, the public usages of that method may limit the input to the private method in some manor or map it in a way that offsets edge cases, i.e. an edge case for the public method may not exorcise the edge case in the private method. I'd much rather just test the full input range and edge cases of the private method directly than leave it up to the next person who comes along. The next person may need to tweak the application logic in the public method and then also figure out how to map the application logic inputs to ensure the edge cases of the private method are tested, seems less than efficient and also prone to being missed. It seems silly and fragile to expect that person to figure out the edge cases for the public method, all the edge cases for private methods and the map of public method input to edge case in private method(s).
It's harder to have these layered effects in other languages (in my experience) though often distinctions between a concrete and abstract (interface driven) API are a good first layer.
Unfortunately, most languages today have at best limited support for this idea. For example, plenty of languages with OOP tools offer both some sort of public/private distinction for access control and a concept of implementing one or more explicit interfaces, but these are usually treated as orthogonal features.
As long as you’re working in that kind of framework, or with more dynamic tools where you don’t necessarily have any concept of explicit interfaces at all, it’s going to be difficult to write unit tests without circumventing the normal access control and modularity mechanisms somehow.
It took me a good year and half to properly appreciate well-optimized, well-tested code in production, and enjoy the challenges associated there (which aren't trivial-- the neatest, ugliest NumPy script can be entirely useless at scale, or in a timeframe useful to users).
But I still personally feel flat when the knowledge gains associated with a Spike aren't as frequent. How have other developers and math-types dealt with that disappointing feeling of a plateau? Besides just changing jobs entirely, I mean.
I'm not entirely sure if I understand you, but it sounds like we are thinking about the same thing.
I've started a job working with C# about 8 months ago. It is a comfortable language to work with. But this job, it's problems (the ones we solve) and C# haven't really exposed me to anything new, which leaves me feeling unfulfilled.
Is that what you are talking about?
On a side note. You will never learn TDD from a video. If you want to learn TDD, get very familiar with testing in general, pick a domain you are comfortable with and then force yourself to write tests for everything before code. Repeat 2-3 times and you will be better at TDD than 80% of the people who are presenting on it.
I can't rigorously prove this, but I suspect that also points at having a framework that is going to tend to gratuitously break your code frequently if you try to upgrade, because they'll have no tests for telling if they have. And neither do you.
http://blogs.perl.org/users/ovid/2013/04/how-to-be-agile-wit... http://blogs.perl.org/users/ovid/2013/04/when-must-you-test-... http://blogs.perl.org/users/ovid/2013/04/code-evolution-vers...
(Case in point, Peter Norvig comments on a famous series of TDD blog posts that had the goal of writing a Sudoku solver, but ultimately failed:
"And I could see, right from the start, you put these two together, and you could solve this Sudoku thing. He didn’t know that so he was sort of blundering in the dark even though all his code “worked” because he had all these test cases."
http://pindancing.blogspot.com.au/2009/09/sudoku-in-coders-a...)
I mix between TDD and not writing tests until after a section of the code is done. It really depends on what I'm trying to accomplish. If I'm spiking things, I tend to just write tests to confirm my work and make sure I'm sane. Other times I know exactly what I want and write the tests first then code. Really just depends on the situation.
Good line. A lot of people will shoot back with "in the long run, it will cost you more" but the point is - a startup doesn't even know if there is a long run.
This is true if you don't have tests or your tests lack. But if you write tests as you work or a little ahead of yourself, the cost difference will probably be negligible. To each his own.
TDD isn't lean. Writting tests for things that were already proven is.
Overtesting isn't lean, but TDD is a practice which fits very well within lean (particularly, its a practice which prevents waste by doing things that aren't needed, since it concretely bounds what is necessary, as well as the more-often discussed effect of preventing additional downstream waste in the form of rework because of incorrectly implemented work that gets into production.)
> Writting tests for things that were already proven is.
I'm not sure what that sentence means; it sounds like something decidedly non-lean (if something is "already proven", writing tests for it seems to be a task known to be redundant, and thus pure waste.)
What do you mean by this?
See the comment somebody made elsewhere about "money driven development". The cost-benefit ratio has to make sense.
If you have a better design tool in your arsenal, feel free to use it, but before you slam TDD as a cult, I'd ask what is your design process and why/how is it better than TDD?
Finally, I'm not interesting in raving about design processes. I am interested in hearing about how other technical people work. What design process do you use? Why do you prefer it to TDD? How have you gone about comparing the 2?
Hyperbole is a far cry from "a slam".
>Who are "these" people?
The ones writing articles like this one.
>what studies are you referring to?
http://theruntime.com/blogs/jacob/archive/2008/01/22/tdd-pro...
>Finally, I'm not interesting in raving about design processes.
But you are the only one doing so? The rest of the paragraph that follows that sentence for example. Most people do not have a religion they call a "design process".
1. How do you determine what is a "unit" that should be covered by a test? 2. How do you determine what a given method/function's responsibilities are? 3. How do you determine where to put a given method or function? 4. How do you get feedback on if your design decisions are correct?
You've also yet to tell me what it is specifically that you don't like about TDD. I'm truly not trolling, I'm curious whenever anyone has such vitriol for a given software technique.
As for the linked article (and the underlying primary research) criticizing TDD, I had read both of those before and I think they speak to a problem with the primary research more than addressing TDD in any significant way (that's not to say you couldn't design a study that did prove TDD was harmful).
Finally, are you being ironic when you say that the original posted articles author is being cultish about TDD? The article specifically tells it's readers not to use TDD. I don't get how that is cultish.
Clearly you intended to have the nuanced view that sometimes TDD is ok and sometimes you shouldn't use it. I guess I mistakenly read the author to say that when in fact you were the one with this point of view all along. My mistake.
I've frequently heard criticism of TDD most loudly by those who don't write unit tests, do unit tests manually, or intend to write them "after they get everything working." This last excuse is most egregious because one rarely has time to go back and write tests for something that is working because there will always be something more pressing than addressing the minor technical debt of writing "unneeded tests."
No, it is entirely orthogonal to having unit tests, that's exactly what I was saying. There is absolutely no evidence to support the claims that TDD is more effective than just being a normal developer who writes tests as needed.
>This last excuse is most egregious because one rarely has time to go back and write tests
Says who? I think it is pretty absurd to invent fake problems to try to justify something. If TDD were actually useful, proponents wouldn't need to compare TDD with "don't write tests at all because obviously the only way to end up with tests is using TDD".