Slashdot Mirror


Writing Unit Tests for Existing Code?

out-of-order asks: "I recently became a member of a large software organization which has placed me in the role of preparing the Unit Test effort for a component of software. Problem is that everything that I've read about Unit Testing pertains to 'test-driven' design, writing test cases first, etc. What if the opposite situation is true? This organization was writing code before I walked in the door and now I need to test it. What methodology is there for writing test cases for code that already exists?"

5 of 86 comments (clear)

  1. do the simplest thing - write tests as u need them by djfuzz · · Score: 4, Informative

    You have to write tests as you change features. Lets say you have a simple change, tweaking a short method to do something different. First you write a test for its exisiting functionality, make sure it passes. Then add a test for the new functionality, run it and watch it fail. Make your change and make the test pass. This would also be the point where you can do some refactoring or clean up, or extend the test to catch boundy conditions.

    With legacy code, you just have to start writing tests with the code as you go, writing tests for functionality that you need to understand or review. If you try and take x number of weeks to write test cases, your doomed to fall behind and have obsolete tests when you are dumb.

    Also, see Working Effectively With Legacy Code by Michael Feathers --> http://www.amazon.com/exec/obidos/tg/detail/-/0131 177052/002-8698615-6720004?v=glance

  2. Re:Unit test the bugs you need to fix by s100w · · Score: 2, Informative

    Based on experience with existing projects, this is the way to go -- write unit tests for bug fixes and new features. It's an overwhelming, time-consuming, job to write unit tests for a big mass of existing code. I also find that once I get going, I end up throwing away or heavily refactoring a lot of legacy code anyway. So if I had written tests, I'd be throwing them out, too. End-to-end system test, even superficial ones, have more value. I will say that sometimes writing tests can help you understand messy old code. You might want to check out "Working Effectively With Legacy Code" by Michael C. Feathers. It's got some good stuff.

  3. My Experience... by Dr.+Bent · · Score: 5, Informative

    I inherited a 1000 class Java based toolkit from my predecessor, which had exactly zero unit tests. Over the last two years, we've made a sustained effort to employ Test-Driven Development and add more tests to ensure that everything works as advertised. As of today the toolkit has over 830 tests, with line coverage of 61% and class coverage of 96%. We've still got a long way to go, but were much better off than we were. Here's how we got there...

    1) A lot of people are going to tell you that you need to write your tests from scratch. That you should assume that your code is broken and work out the expected results by hand and create the test assertions accordingly. I disagree. If you're testing old code, it's much more useful to use the test to ensure that it does whatever it did before, instead of ensuring that it's "correct". I prefer to treat the code as though it is correct, and build the tests around it. Even if the assumption is occasionaly wrong, you can make the tests much quicker this way. That allows you to refactor and extend your system with confidence, knowing that you haven't broken anything. Remember, TDD isn't really about quality assurance, it's about design and evolving design through refactoring. More tests == more refactoring == better system.

    2) You're probably not going to get a lot of extra time to sit around and write tests. You need to captialize on the time that you have and turn problems into oppertunities to add tests. Whenever you find a bug, make a test that reproduces it. If you need to add supporting stub or mock objects, consider making them reusable so that future tests will be easier to write.

    3) If you need to add new functionality to the system, just follow the standard TDD steps of Test->Code->Refactor, and make sure that you add tests for anything that might be affected by the change.

    4) I'm assuming that you already have a continous integration build that runs the tests, but if you don't, make one. Now. Also consider adding other metrics to the build like code coverage (we use Emma), findbugs, and jdepend. These will help you track your progress and can be very useful if you have to defend your methdology to people who view TDD as a waste of time (The Code Coverage to Open Bugs ratio gets them every time).

    5) In general, you need to look for oppertunities to write tests. Don't understand how a module works? Write a test for it. Found a JDK bug? Reproduce it with a test. Performance too slow? Use timestamps to ensure that the performance of a alrorithm is in a reasonable range.

    You've probably got a long road ahead, but it's worth the work. Keep at it, and good luck.

  4. Re:Start With the Documented Requirements by dwpenney · · Score: 5, Informative

    Ok. I am confused by this. There is a distinct difference between System Test cases and Unit Test cases. If you are working from a design document detailing the requirements from a working Business or Systems Requirements document and testing the items to make sure that requirements are met you are performing a System Test - a test at a much higher level than Unit Testing. At the Unit Test level you are checking the boundries in the code itself to make sure that loops are exited correctly and logic is performed correctly. In essence a Unit Test is at a component level, intended to look inside the component and make sure that it operates correctly based on it's very limited sets of inputs and outputs. At a higher level, System Test cases look at how the component interacts with other components and wether this interaction meets the requirements. Stupid example using web code: Putting ^5$*1@ in a text search input box is a Unit test case makeing sure that the entry parameters if this code will not barf on the input. System test cases should not need to test this (with a proper procedure for unit testing) but should instead be focusing on if search results are in line with what was requested. These necessary definitions limit and focus the level of your testing and the understanding of the question that is being asked.

  5. I recommend this article by gstover · · Score: 3, Informative

    A recent article in print about automated unit tests for legacy code was

    "Managing That Millstone"
    By Michael Feathers
    Software Development
    January 2005
    http://www.sdmagazine.com/documents/s=9472/sdm0501 c/sdm0501c.html

    It included suggestions for how to inject unit tests into code which isn't loosely coupled, some tips on how to refactor to get loosely coupled interfaces, & what you can do when neither of those approaches will work. It was a valuable & enjoyable read for me, at least.

    gene