When I first heard about XP, I wanted to try it. Being a C++ programmer, the first step involved porting KentBeck
's JUnit to C++. I dubbed it CppUnit
. Secondarily, I needed to figure out where I was going to start.
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 UnitTest
s for it. Based upon that experience, I tend to think that reuse can be handled by writing UnitTest
s for existing things, and by writing UnitTest
s 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
). 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 UnitTest
s 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 UnitTest
s 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 UnitTest
s, 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.
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 UnitTest
s 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 UnitTest
s? 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
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...
- report the bug to the vendor,
- work around the bug, and
- write a UnitTest that illustrates the bug.
is documentation; it reminds you that you have to keep working around the bug when writing new software and maintaining the existing code. It also helps notify you when the vendor fixes the bug, and to verify that it is indeed fixed.
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...
- Report the bug to the vendor.
- Make UnitTests for the code you are about to change.
- Use a source code control system to keep track of the original source from the vendor, and your changes to it.
- Make the smallest change possible. (Yes, I know their CodeSmells, but you don't really want to take over their job and abandon all hope of accepting upgrades from them later, do you?)
The vendor will, eventually, release a new version of the library. What are you going to do then? You have to know exactly
what changes you made, so that you can repeat them, to fix the bugs that the vendor still
hasn't bothered to fix.
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
- What's to be gained from doing a lot of testing on a 3rd party library that you cannot change?
The most obvious benefit is that it can save you from writing a lot of code to a library that ultimately proves to be unusable. Since you need to evaluate a third-party component before you invest a lot of time and money in it (unless the component has such a reputation that there is no question of its suitability for your project), why not conduct this evaluation in such way that it produces UnitTest
s as a side effect? (It may not be cost-effective to product UnitTest
s for the third-party component that are as comprehensive as UnitTest
s one would write for code developed in-house, but it's still something. Technically, I suppose, that means they're not "UnitTest
s" if they don't test the component completely but they are, uh, er, AlmostUnitTests?
, which, as pointed out elsewhere on this site, is another way of saying NotUnitTests?
. But they're still useful tests.)
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
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.