8 ms·
From the introduction in the article, about the merits of TDD: As well as the simple fact that your code will be more reliable, it forces you to write your cod
by Chris_Newton 13y ago
From the introduction in the article, about the merits of TDD:
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.
- JulianWasTaken 13y agoMichael Feathers has a good talk where he discusses this a bit: http://vimeo.com/43659006 http://vimeo.com/43659006
- rst 13y agoOne trick I've used in Scala code is to put the test suite in the same package as the code under test, which gives the tests access to package-private stuff which are normally inaccessible to other code. That probably could work for Java as well. (And in C++, I guess you could declare the test suite as a "friend" of the code under test --- "C++, the language in which friends can access your private members!".) 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...
- gbog 13y ago> challenge any claim that using something like TDD necessarily implies that your code will evolve a robust, modular design. 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.
- kasey_junk 13y agoChanging production code purely to support test code is most certainly a design smell and almost always indicates that incorrect abstractions are being used. 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.
- warmwaffles 13y ago> 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++ 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.
- mbell 13y agoI use groovy for writing tests. Much better language for writing tests in general in my opinion but also has the benefit of ignoring 'private' so you can directly test your private methods in isolation without messing around with reflection. 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).
- tel 13y agoThis "layered" effect of interface hiding is natural in pure functional contexts. In Haskell you often see exposed "Internal" modules for just this reason---they're not the primary API, but they're valuable to have around. 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.
- Chris_Newton 13y agoThere is a lot of merit in allowing modules to offer multiple interfaces that clients can use as long as they explicitly request them. You can solve the multiple-levels-of-abstraction API problem that way, and you can solve production/test/debug access control problems that way too. 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.