I needed to add some new capabilities to my software. The new capabilities paralleled some capabilities that the software already had. My best bet was to develop the new capability, existing in parallel with the old, and slowly migrate it in (I've heard this called ArchitecturalSubstitution). However, to get my feet wet, I started to test a few classes that were already there. I noticed immediately some coupling that I didn't like and I worked to get rid of it. After doing that, I started testing other classes that I had and each time I learned a little more about some entangled dependencies that were not really necessary. Although reality set in and I had to get back to adding the new capability, the lesson was not lost on me: Even though it is better to write UnitTests before you code, you can learn a lot about existing code by writing UnitTests for it.
I got a bit more of this insight later. I was using a component that someone else had written and the source was not readily available. I had a spec sheet with the signatures of all of the component operations. I knew the guy who wrote it, and I'd used it in another context. To see how well it might serve me now, I started writing UnitTests for it. Based upon that experience, I tend to think that reuse can be handled by writing UnitTests for existing things, and by writing UnitTests for adapters on existing things. The big question is "how much do I trust what I am reusing?" Trust can be based on many things, many of them outside of the code realm. Sometimes tests are not enough to instill reuse confidence. Sometimes they are.
Now, is this type of reuse Simple? Or Easy? (see DoTheSimplestThingThatCouldPossiblyWork and SimpleIsntEasy). The decision to use a black box and adapter rather than writing something from scratch is significant. Adapters uglify code, but on the plus side, you didn't have to write the code, and you have test cases that tell you (you hope) enough to be able to use the component with confidence. Moreover, because UnitTests can act as specifications (UnitTestingIsDesign), you can always revisit your decision later and rip out the adapted component... provided you replace it with something that keeps the tests running.
Even with access to the source code (white box?, opaque box?), writing experimental applications or UnitTests seems a good idea - the ScientificMethod as a learning tool. -- ScottJohnston
A white box is simply a better-documented black box. If you had read-only access to the source code, you'd have a transparent box; if you can modify the source code you have an open box. A transparent box is also where the source code is supplied with a library, but you still have to manually email changes back to the original author, who may or may not decide to incorporate them. Open box is where you have a CVS repository or similar that you can make changes to. At least those are my definitions. -- GavinLambert
Yes, this aspect of UnitTests, to assist a learning process, I think is an interesting point. In my (still ongoing) process trying to learn C++ I have written quite a few small code snippets to illustrate particular behaviours. As long as my use of C++ is covered by this testing, my general C++ development should be ok. Discrepancies may be either due to my code not following the standard (I try to!), or due to a non-standard-conforming (not uncommon with C+) or buggy compiler. -- ThorsteinThorsteinsson?
I would like to bring up something slightly different.
Programming without the use of libraries and other components is not possible in any reasonable size project. So there is a codebase that you have to trust in. Sorry enough, most libraries ship without any test framework. The question really is: How do you integrate existing or foreign code into your own code? Do you just use it and trust that it's bug free? I would say No. How do you write the tests? And what tests do you write?
Should I write UnitTests for all library functions I want to use? That would create a test framework for the library really, which is not my job. It is way too much work as well. And if I find a bug in the library, I often can't fix it anyways. (no source)
Should I write replacement methods for the library I use to generate working UnitTests? Should I use Adapters to attach the library and insert some kind of test there?
What do I do if my code breaks because of a used library? Sometimes I can program a workaround, but that's not always possible.
That is a lot of questions.... What are your ideas or experiences? -- MalteKroeger
To DoTheSimplestThingThatCouldPossiblyWork would be to use the library without testing it. What's to be gained from doing a lot of testing on a 3rd party library that you cannot change?
Now, once you start using a library and find a bug in it, you should...
This may not be ExtremeProgramming, but it's what I do: If you have the source code for the library, and find a bug, it might make sense for you to fix it yourself. But you must be absolutely sure to...
I don't like changing 3rd party code. It's dangerous, and can easily get out of hand. But sometimes you have no other reasonable options. -- JeffGrigg
From this point of view, tests on a 3rd party library are like AcceptanceTests?.
I totally agree with Jeff. But I wonder if XP encourages code reuse. When I always just DoTheSimplestThingThatCouldPossiblyWork it might be simpler to code a piece of software myself instead of understanding and using a 3rd party library. But in the "long" run, I will do better using the library. So where are those "long term" decisions in XP? -- MalteKroeger
If I understand XP correctly, it does encourage reuse. OnceAndOnlyOnce discourages rewriting existing functionality and if you RefactorMercilessly you'll end up with a lot of code reuse. Coding a piece of software yourself might be easier than using a 3rd party library (or, for that matter, other pieces existing code) but I don't think it's the simplest thing that could possibly work (see SimpleIsntEasy and discussion above). -- OliverKamps
How do you define software reuse? Is it using a fixed capability from many places? Is it branching the development of pre-existing code? Is it the factoring of object-oriented methods? Is it a combination of these and more? Under EvolutionaryProgramming RonJeffries said XP wasn't about maximizing software reuse, but about maximizing developer productivity. -- ScottJohnston
I see it like this. If you're going to use a library API that you haven't used before, especially one of those Microsoft or Java APIs with the incomplete documentation, you will inevitably write little programs to see what the API actually does in the situation you need it. Why not write that into a test object? You'd then have a test to make sure that when the library changes, your API still works the way you expected. In fact, if we all did this a lot, then perhaps Microsoft's service packs would come out sooner with better beta testing. We'd just run our test suites to tell them if they got it right. -- JohnDuncan
In an exchange on the e-group on reuse UncleBob said he would import a previously used piece of code with its tests, KentBeck said he would re-write it (the ChuckMoore Forth approach).
The SimplestThing for me is to choose a third-party library or component, rather than implement my own, if this component conforms to a well-known API, or an open standard, or otherwise avoids lock-in. I'm a big fan of XML as a result of that policy.
This page mirrored in ExtremeProgrammingRoadmap as of April 29, 2006