We have a large, multi-platform application written in C. (with a small but growing amount of C++) It has evolved over the years with many features you would expect in a lar
My little experience with legacy code and introducing testing would be to create the "Characterization tests". You start creating tests with known input and then get the output. These tests are useful for methods/classes that you don't know what they really do, but you know that they are working.
However, there are sometimes when it's nearly impossible to create unit tests (even characterization tests). On that case I attack the problem through acceptance tests (Fitnesse in this case).
You create the whole bunch of classes needed to test one feature and check it on fitnesse. It's similar to "characterization tests" but it's one level higher.
G'day,
I'd start by having a look at any obvious points, e.g. using dec's in header files for one.
Then start looking at how the code has been laid out. Is it logical? Maybe start breaking large files down into smaller ones.
Maybe grab a copy of Jon Lakos's excellent book "Large-Scale C++ Software Design" (sanitised Amazon link) to get some ideas on how it should be laid out.
Once you start getting a bit more faith in the code base itself, i.e. code layout as in file layout, and have cleared up some of the bad smells, e.g. using dec's in header files, then you can start picking out some functionality that you can use to start writing your unit tests.
Pick a good platform, I like CUnit and CPPUnit, and go from there.
It's going to be a long, slow journey though.
HTH
cheers,
We are in the process of doing exactly this. Three years ago I joined the development team on a project with no unit tests, almost no code reviews, and a fairly ad-hoc build process.
The code base consists of a set of COM components (ATL/MFC), cross-platform C++ Oracle data cartridge and some Java components, all using a cross-platform C++ core library. Some of the code is nearly a decade old.
The first step was adding some unit tests. Unfortunately, the behaviour is very data-driven, so there was some initial effort in generating a unit test framework (initially CppUnit, now extended to other modules with JUnit and NUnit), which uses test data from a database. Most of the initial tests were functional tests which excercised the outermost layers and not really unit tests. You will probably have to expend some effort (which you may need to budget for) to implement a test harness.
I find it helps a lot if you make the cost of adding unit tests as low as possible. The test framework made it relatively easy to add tests when fixing bugs in existing functionality, new code can have proper unit tests. As you refactor and implementn new areas of code you can add proper unit tests which test much smaller areas of code.
In the last year we have added continuous integration with CruiseControl and automated our build process. This adds much more incentive to keep tests up-to-date and passing, which was a big problem in the early days. So I would recommend that you include regular (at least nightly) unit test runs as part of your development process.
We have recently focussed on improving our code review process, which was fairly infrequent and ineffective. The intent is to make it much cheaper to initiate and perform a code review so that developers are encouraged to do them more often. Also as part of our process improvement I am trying to get time for code reviews and unit tests included in project planning at a much lower level in a way that ensures individual developers have to think more about them, whereas previously there was just a fixed proportion of time devoted to them that was much easier to get lost in the schedule.
One approach to consider is to first put a system-wide simulation framework in place that you could use to develop integration tests. Starting with integration tests might seem counter-intuitive, but the problems in doing true unit-testing in the environment you describe are quite formidable. Probably moreso than just simulating the entire run-time in software...
This approach would simply bypass your listed issues -- although it would give you many different ones. In practice though, I've found that with a robust integration testing framework you can develop tests that exercise functionality at the unit level, although without unit isolation.
PS: Consider writing a command-driven simulation framework, maybe built on Python or Tcl. This will let you script tests quite easily...
There is a philosophical aspect to it all.
Do you really want tested, fully functional, tidy code? Is it YOUR objective? Do YOU get any benefit at all from it?.
yes, at first this sounds totally stupid. But honestly, unless you are the actual owner of the system, and not just an employee, then bugs just means more work, more work means more money. You can be totally happy while working on a hairball.
I am just guessing here, but, the risk you are taking by taking on this huge fight is probably much higher than the possible pay back you get by getting the code tidy. If you lack the social skills to pull this through, you will just be seen as a troublemaker. I've seen these guys, and I've been one too. But of course, it's pretty cool if you do pull this through. I would be impressed.
But, if you feel you are bullied into spending extra hours now to keep an untidy system working, do you really think that that will change once the code gets tidy and nice?. No.. once the code gets nice and tidy, people will get all this free time to totally destroy it again at the first available deadline.
in the end it's the management that creates the workplace nice, not the code.
I think, basically you have two of separate Problems:
Modularization, refactoring, inserting Unit tests and alike is a difficult task, and i doubt that any tool could take over larger parts of that work. Its a rare skill. Some Programmers can do that very well. Most hate it.
Doing such a task with a team is tedious. I strongly doubt that '"forcing" developers' ever will work. Iains thoughts are very well, but I would consider finding one or two programmers who are able to and who want to "clean up" the sources: Refactor, Modualrize, introduce Unit Tests etc. Let these people do the job and the others introduce new bugs, aehm functions. Only people who like that kind of work will succeed with that job.