6 ms·
> Writing tests before the implementation code implies that you are certain about your final API, which may or may not be the case. How does this myth continue
by agentultra 9y ago
> Writing tests before the implementation code implies that you are certain about your final API, which may or may not be the case.
How does this myth continue to persist?
Writing the test first has nothing to do with knowing the final API. When I write tests first I am looking for an API. The tests help guide me towards a nice API.
I personally find TDD in this manner works best when taking a bottom-up approach with your units. I start with the lowest-level, simple operations. I then build in layers on top of the prior layers. Eventually you end up with a high-level API that starts to feel right and is already verified by the lower-level unit tests.
Although this style of development has become less prevalent in my work with Haskell as I can rely on the type system to guarantee assertions I used to have to test for. This tends to make a top-down approach more amenable where I can start with a higher-level API and fill-in the lower-level details as I go.
- tome 9y ago> > Writing tests before the implementation code implies that you are certain about your final API, which may or may not be the case. > How does this myth continue to persist? Yes indeed. TDD helps you find a nice API.
- barrkel 9y agoTDD encourages you to implement an overly complex API with too many possible configurations. It encourages delivery of a box of parts, rather than sub-assemblies.
- spronkey 9y agoIMO TDD has almost exactly zero to do with the design of your API. You can TDD top down by writing an end to end test, and recursing TDD down each level until you get to a single unit, repeat until all inception layers of the tests pass. You can TDD bottom up, by starting with components you'll know you'll need, working out some APIs at their level, then doing the same to piece together larger components. Ultimately you're in control of how your API looks, and this has nothing to do with TDD itself - if you get caught up exposing a bunch of inner workings and configurations, then you're simply not designing well. It has nothing to do with TDD. Likewise, if you get caught short with an API that's too simple, well, that has little to do with TDD either.
- tome 9y agoTo my respondents, who may have read more into my comment than I intended, I merely wished to support agentultra's claim that > When I write tests first I am looking for an API. The tests help guide me towards a nice API. and to counter the article author's claim that > Writing tests before the implementation code implies that you are certain about your final API, which may or may not be the case. I did not mean my comment to be interpreted as "using TDD will guarantee you find an nice API". If you prefer, please interpret my comment as "TDD (under certain circumstances and when used in an appropriate way) can help you find a nice API".
- kalecserk 9y ago> I start with the lowest-level, simple operations. I then build in layers on top of the prior layers. I was discussing this with a coleague this week: starting by the lower level details vs starting from the more abstract/whole api. I prefer to start by writing a final API candidate and its integration tests and only write/derive specific lower level components/unit tests as they become required to advance on the integration test. My criticism on starting with the bottom-up is that you may end up leaking implementation details in your tests and API because you already defined how the low level components work. I have even seen cases where the developer end up making the public API more complex than necessary due to the nature of the low level components he has written. Food for thought!
- 0x445442 9y agoI agree with this approach. It seems that one of the consequences of "Agile" is a general reluctance to invest a fair bit of time upfront thinking about interfaces. It's as if the interface design process somehow became associated with documentation, regardless if any non-code documentation is ever created.
- ljm 9y agoI'm not convinced this is a consequence of agile in as much as it is more a consequence of bad pragmatism or expectation management. In terms of setting expectations, you are not doing anybody any favours by rejecting some up-front thinking in favour of rolling with the punches. And in the same way, you are not being pragmatic by cutting every corner you possibly can - pragmatism is just as often about deciding when to spend time up front to save time later on, and not just sacrificing everything from the short term (which I think is a common and valid criticism laid against poorly implemented agile workflows). That said, I don't think TDD is a good way to figure out your API. It has its value for sure, but not if you take a purist or dogmatic attitude towards it. In any case, it seems to assume that more tests are always better (hence you start every new piece of code with a corresponding test), when I'd argue that you want a smaller suite of focussed and precise tests.
- 9y ago
- _asummers 9y agoThere's methodologies in TDD that let you go explore to find the ideal API by working with it, then going back and removing that code (like you would a prototype) and TDDing it afterwards with the destination in mind. I personally think much more easily with this approach. I have to "play with it" and know where I'm going before I can test towards that goal. I think they call it spiking, but I might be misremembering the term.
- kalecserk 9y agoCheers for bringing me back memories of “Spike solution” from the early 2000s :) http://wiki.c2.com/?SpikeSolution http://wiki.c2.com/?SpikeSolution
- AstralStorm 9y agoCrucial part of rapid development is to get a prototype. The problem is that in many cases management tries to push prototypes into production as quickly as possible and you might get stuck with the mistakes. Forever in the worst case.
- coldtea 9y ago>Writing the test first has nothing to do with knowing the final API. When I write tests first I am looking for an API. The tests help guide me towards a nice API. Yeah, it means for people who don't have the time to rewrite the code and the tests multiple times.
- rukuu_001 9y ago> Writing the test first has nothing to do with knowing the final API. When I write tests first I am looking for an API. The greatest and most poorly communicated benefit of TDD right there.
- bluGill 9y agoNo, it is an anti-pattern. TDD will get an API, and it won't be the worst possible API. However TDD does not do anything to ensure the API is good. People who think TDD creates a good API probably have good instincts and a problem where an acceptable API is easy to create. When you have a complex problem that will have hundreds of users (thus it demands not just an okay API but the best) you need to think about the API first. Otherwise you can design yourself into a corner where to fix the API you have to change all your tests. Once you have the API design in place TDD is a great tool for refining it. You need real working code using an API to sake out all the details and TDD is a great way to figure what you missed. You need a direction in mind first.
- agentultra 9y agoTDD does not do anything to ensure the API is good. Well, what is good? You suggest such an API has to solve a complex problem with hundreds of users. I should think that I've written a few modules with just such an API using TDD methods. So what are we really saying here with this definition? Your elaboration goes back to the myth I would like to see dispelled as my experience, and that of many others, has demonstrated that it's a bit of a red herring. When I start a module I don't know what the API should be or what invariant is important or anything. All that test driven development asks is that I first make an assertion about my problem and try to prove that it's true (or not as the case may be). This leads me to good API designs because the tests invariably contain the assertions about my invariant, they document the API at a high level, and they demonstrate that my implementation is correct as to those assertions I made. If I am thoughtful in this process I know that some assertions contain a quantification over a universe of inputs so I use property based tests to drive my specification. Other times a handful of examples is good enough to demonstrate correctness and so I simply test the units with them. And the API is entirely under my control this whole time. The tests are guiding me to a good API that does exactly what I say it will do by way of executable specifications that cannot be wrong about my implementation (or else the test would fail). There's nothing in the above that says I cannot take a top-down approach and design my module's interfaces first. Nothing. Personally I prefer the bottom-up approach for many of the reasons I explained in my OP. I like to define things in small units that I can compose over with abstractions that let me achieve more interesting results. Testing keeps me honest as to the true requirements so that I don't go off into the woods.
- hvidgaard 9y agoI'm the exact opposite. I start with the highest level integration tests to test the API out. I iterate on this process quite a bit until the API is more or less settled down, and then I start implementing the API with appropriate unit tests. As much as I'd love a very cleanly implemented API, in the end many more devs will be using the API, so the use of it has priority over an API that is easier to implement.
- reacweb 9y agoYour comment resonates a lot with me. The bottom-up approach is my favorite, but I think most people are more top-down. When a project has unit tests, its architecture is more pleasant for me. There is also less coupling between classes, this makes the code more reusable. TDD has clearly an influence on the style of the code. I think it forces the code to be better with better API.
- bluGill 9y agoI find TDD results in tighter coupling. The problem is we found dependency injection and a mock framework early and never looked back. As a result we inject everything everywhere, and all my classes know about all its collaborators and are tightly coupled. Sure we have a mock and so technically we are just coupled to the interface, but the result is the same: change one change the other.