TDD: Does this interfere with good API design?

I never wrote TDD code, but I saw a decent discussion about it here on SO. My biggest concern about this is that it seems that the overall design of the API (for flexibility, ease of use, simplicity of the interface and performance) sometimes takes the back seat to make the code mock-up, ultra-modular, in addition to what is needed for any use case API, etc. For example, TDD proponents often suggest that things be passed as parameters that, in terms of API abstraction, the called method should “just know” or that classes and methods will be taken into account in such a way as to simplify testing, which is not always the way that works best refers to a problem area.

For people more experienced with both TDD and API design: will you find that TDD often interferes with good API design? If so, how do you deal with this?

+45
design api tdd
Dec 08 '09 at 16:00
source share
14 answers

I have been using TDD for several years now and I have found that it manages the design of the API for a more convenient design, giving you two different clients for the API from the start; you have production code and test code, both of which want to manage the API in different ways.

It’s true that sometimes I add things to simplify the testing of the API, but I almost always think that the things that I think I just use for testing are actually very useful for monitoring purposes. So, for example, FooAllocator may end with an optional constructor argument, which is the monitoring interface ( IMonitorFooAllocations ), which is very useful for bullying during testing so that I can look inside, but which also tends to be very useful when you suddenly find that you need set some distribution indicators for the rest of the world during production. Now I tend to think about additional bits that I can add to provide easy testing in terms of their dual use for additional product monitoring. I generally write server code and can expose internal things, as perfmon counters are VERY useful ...

Similarly, you correctly say that often the objects that make up the API can take some other objects explicitly and not stretch them and get them from a known place, but that's good. Believe me, once you get used to explicit dependencies, you won’t want to go back to digging a class after a class to find out how and why your Widgets accesses the active directory when there is no hint in the API that they want to do this. It also often happens that you break these dependency relationships during design and testing, and then hide them again when you connect all the parts. You are still “parameterizing from above”, but more often than not, the API object model can mean that you never see “above” as an API user. You get one place to configure the API with the things that it needs, and often this is no different from what it would look like if you had a lot of singles and globals and hidden dependencies.

But remember, TDD is a tool when it does not fit, do not use it.

+10
Dec 08 '09 at 17:03
source share

No, I think TDD usually encourages good design. Things that are easy to check are often easy to use in production ... because when you come up with a test, you think, "What would I like to do next?" and then make it work.

When you practice TDD, you are forced to think of “use cases” for the API, how the programmer will use classes / methods, and you will end up with a very useful structure. If you need to create hundreds of FooFactory and BarAuthenticator s, or the API becomes “ultra modular”, as you say, you will most likely understand that when writing test code and think about how to simplify it.

Regarding options and dependency injection - I usually find that dependencies become much clearer using TDD. They are usually constructor arguments, not method arguments, but make it clear that an authenticator, a source of randomness, etc. is required to implement the API. Useful for understanding what he is doing. You can be sure that the code will not get to the network or the remote database somewhere, because it hides these things in its implementation. These hidden details are what make the API difficult to use in a test environment.

Note that when dependencies are part of the constructor call, this is not part of the interface that the class can implement - dependencies inside the interface are usually hidden, but TDD means that the implementation provides them in an obvious way.

A good source of information on dependency injection and TDD can be found on the Google Testing Blog . Pay special attention to Miško Hevery posts or watch some of his videos on Youtube: Don't look at things anymore .

+32
Dec 08 '09 at 16:06
source share

TDD leads to an emerging design that ultimately creates a very convenient and extensible API. TDD is not testing. It's about creating seams in code that you can use to add behavior when you learn more about your project.

+12
Dec 08 '09 at 16:09
source share

TDD is an API design technology. Each time you write unit test, you either create the API for the first time, or use the previously created API. Each of these tests allows you to “feel” how easy or difficult it is to use the API. Each new test forces you to view the API from a new perspective. There can be no better way to develop APIs than to exhaustively use them, as TDD forces you to.

In fact, this is the reason why TDD is considered a design technique rather than a testing technique. When you practice TDD, you are not designing your APIs in a vacuum. You design them using them!

+12
Dec 31 '09 at 14:39
source share

Most of the things you pointed out as TDD flaws are considered by many to be good design elements.

Given that the idea is conveyed instead of simply “knowing” the method, the latter often leads to singleton or other anti-cohesive projects.

While it may be true that you can get a design in which some aspects exist only to support testing, this is not necessarily a bad thing.

However, as a rule, if you need to rethink your design in order to make it verifiable, you will usually come to a design that is better in terms of cohesion and suitability for the purpose - while still being flexible in the sense that it can easily be replaced by any of your future needs through refactoring, because you have tests to give you confidence in a quick change.

+7
Dec 08 '09 at 16:08
source share

I use TDD and I think that it works well in relation to building APIs, but I think that APIs, especially those that you provide to external clients, are an area in which you need a slightly more advanced design than typical when using TDD, TDD relies as much on refactoring as it does on test one. With the API, you do not always have the luxury of refactoring your method signatures as well as of pure design. If you are not careful in designing your interface, you may find yourself with an ugly interface that supports several methods that do very similar things to maintain backward compatibility with your existing API and at the same time move your project forward.

If I have an API that, as I know, I will expose to external users, I usually take more time to think about the signature and get it as close to the "right" as I can before starting the process development. I also often suggest that my customers receive feedback so that we get to a stable interface as quickly as possible. Refactoring behind the scenes to improve implementation without changing the interface is not a problem, but I want to quickly get a stable interface and want to invest more to get it.

+6
Dec 08 '09 at 16:22
source share

With a traditional API design, it’s easy to insert yourself into a corner: you can get an API with many hidden dependencies (for example, for the needs of an AB class it requires C needs D and if you change the order in which the classes are initialized, things start to break down).

TDD ensures that individual parts remain separate. It also allows you to see your API from a very unusual perspective: as a user / consumer. Your first question: "How do I want to use this?" not "How do I want the API to look?" The latter can lure you into a hidden trap, while the former leads to something that I call the "intuitive API": it behaves as expected.

A word of advice: do not make TDD your religion. This is a tool and some problems cannot be solved with some tools. Therefore, if TDD does not work for you for any reason, then this is normal. Use as many TDDs as you want. Do not worry about it. Over the years, you will find your comfort zone.

+4
Dec 08 '09 at 16:12
source share

You can take a look at the newspeak work in ao Gilad Bracha language to see some relevant API and language design.

+1
Dec 08 '09 at 16:10
source share

I agree 100% with the top answers. But actually it depends on what you mean by “good API design”. TDD leads to testable, modular working code, but ultimately the most important aspect of TDD code is testability.

You will find that this leads to a different design than to other processes. You will find that the code under test can expose a few more bits of its internal components, which may seem a pure API.

In many cases, it makes sense to use TDD to create working code, and then - as a separate step - pull out the API. Otherwise, you have two forces that work somewhat in conflict: a simple API and verifiable code.

This is a thin point, however, and overall TDD will provide much better APIs than designing from an ivory tower.

+1
Dec 08 '09 at 16:52
source share

On the contrary, since you use your thinking methods as a user by writing your tests, you will find problems that your user will come up with when you write him. In fact, you will have a much cleaner and more user-friendly interface that you would write differently.

+1
Dec 08 '09 at 18:29
source share

If your API suffers due to the internal requirements of your objects, this is what the facade template is for. Not all objects must be publicly accessible.

Many TDD pain points are essentially signs of pain points in a design. If it is difficult to create a class because it needs to pass 18 dependencies, the main problem is that the class has too many dependencies and will be quite fragile. It is also probably too much waaaaaay. The "pain of TDD" in this case is good, as it makes other problems more obvious.

+1
Dec 11 '09 at 4:06
source share

TDD does not interfere with good API design.

Failure hinders good API design.

Both are not synonyms - I started using TDD myself before the others wrote books on it, simply because it clearly shows if the requirements are verifiable and the projects meet the requirements.

However, I think Mocking is bad because it affects design and implementation and introduces other artificial requirements. It also provides implementation internals that shouldn't be, and makes testing fragile. He checks how something is done, not what is done.

How to counter: use TDD, but do not use mockery.

+1
Dec 31 '09 at 14:46
source share

The point about TDD i sthat we write tests that call the code when it is developed. Therefore, we must get an extremely convenient interface. Of course, this does not happen by chance. We still have to make the right choice.

0
Dec 08 '09 at 16:09
source share

I have not noticed that TDD makes the wrong API options at all.

On the contrary, creating a clear structure, the API, in the first place, is more understandable and more understandable to its users.

0
Dec 08 '09 at 16:12
source share



All Articles