Slashdot Mirror


Retrofitting XP-style Testing onto a Large Project?

Mr Pleonastic submits this query for your consideration: "I work for a small startup (ok, me and another guy comprise the entire development team) that has somehow managed to survive the bust, attract a number of customers, and build up about 300K lines of functionality. Up to now we've made it by being smart and conscientious hackers, but I'm increasingly embarrassed by our shortcomings in testing. I like the XP approach to making enduring, automated test suites, but most of what I read about XP focuses on obvious stuff and changing your programmer culture at the outset. Does anyone have experience with, or advice for, retrofitting it onto a fairly mature project? What do your test suites look like, anyway? The bugs I fear most are of the 'If the user does X and then Y, the result blows away our assumptions' variety, not the 'Oops! My function returned the wrong value' variety (which happens of course). How do you write good test code for the former, without spending even longer debugging the test code? Is XP just for small, new projects?"

49 comments

  1. Somebody had to say it by Sklivvz · · Score: 5, Funny


    Win XP style testing? So, what's so hard? Release an alpha version and call it RC, and let users do the testing...
    </wintroll>

    1. Re:Somebody had to say it by RzUpAnmsCwrds · · Score: 1

      As a user of XP RC2, Microsoft's release canidates are extremely close to their actual product. From XP RC2 to XP 2600, I found no changes (except for the build number and "Evaulation Use Only" missing from the desktop).

    2. Re:Somebody had to say it by dynoman7 · · Score: 1

      As a user of XP RC2, Microsoft's release canidates are extremely close to their actual product. From XP RC2 to XP 2600, I found no changes (except for the build number and "Evaulation Use Only" missing from the desktop).

      You weren't using it very much is what you are saying, right?

      --
      Blarf.
    3. Re:Somebody had to say it by RzUpAnmsCwrds · · Score: 1

      "You weren't using it very much is what you are saying, right?"

      I used it for 180 days, the length of my evaluation period. Then I purchased XP 2600. Virtually no difference.

  2. Use the FailFast principle by Dr.+Bent · · Score: 4, Insightful

    Finding conditions that are outside your assumptions is not something you can do with a unit test. I have found that trying to simulate user creativity (stupidity?) with unit tests is an exersize in futility. Use your unit tests to make sure your methods do what they're supposed to do.

    To find all those tricky combinations of use cases that blow away all your assumptions, just stick to the Fail-Fast principle. If you find anything that goes even slightly wrong, complain. Loudly! Throw an exception, pop up a dialog, whatever you need to make sure that everyone knows an error just occured. This will do two things:

    1) You'll find a lot more errors in your code. You'll also be motivated to fix them quicker because the app will be unusable until you do.

    2) You'll reduce the likelihood of generating bad data. The only thing worse than your program doing something wrong and crashing is doing something wrong and NOT crashing. Users will usually forgive you if your software crashes. If you start giving them bad data, they'll lose confidence in your app and never trust it again.

    1. Re:Use the FailFast principle by Anonymous Coward · · Score: 1, Interesting

      Basically, go to every function, method, block of code and write a piece at the top to check all inputs. At the bottom, verify outputs. If you access a database, avoid autocommit like the plague.
      If ever function is sure it has valid data coming in and valid data going out, nothing the customer can do should be able to break it. Ideally for every function the caller should check the data before it is passed, the callee should verify it is what it expects and verify the data before it is returned and finally the caller should check for success of the call.
      If you want, wrap the checks in defines so in production code if performance sucks you can disable some of the costlier checks.

      Flexibility is good in design, brittleness is good in implementation.

    2. Re:Use the FailFast principle by GebsBeard · · Score: 2, Interesting
      I can attest to the fail fast principle. I've worked in projects where the lead designer mandated "this system must stay up and running even when it loses its marbles (ie memory corruption detected, etc)" and that philosophy only leads to chaos and grief. Much better were systems that utilized runtime assertions - even in production - on the correctness of data passed into functions. Instead of being more crashprone (which I had expected) those code bases quickly became titanium plated. Nothing could break them. At the first sign of trouble the thing would shut down and I had no choice but to fix what ailed it.

      BTW as to unit testing I've had really good luck sticking tests in tight loops and using a random number generator to pump a module to exercise all possible code paths.

  3. Retrofitting is Hard! Refocusing is Easier! by chromatic · · Score: 5, Informative

    This is hard to answer in a short comment. I'll try, though you're welcome to contact me for more details through the information on my website.

    Retrofitting tests onto an existing project is hard. Not only is it tedious, time-consuming work, but you're always haunted by specters that ask "How do you know the test isn't broken?" It's nice to have the tests, but you'll spend a lot of time and energy creating them that could be better spent adding new features and improving existing features. Besides that, it'll likely sap any motivation you might have had for testing.

    It's much easier to draw a line in the sand and say "all new features and bugfixes will have tests, starting now". Before you fix a bug, write a test that explores the bug. It must fail. Fix the bug. The test must now pass. Before you add a new feature, write a customer test that can demonstrate the correct implementation of the feature. It must fail. Add the feature. The test must now pass. From the programmer level, you can write programmer tests through the standard test-driven development style.

    It still can be tricky to get started, especially with customer tests, but they don't have to be beautiful, clever, or comprehensive. They just have to test the one feature you're working on sufficiently to give you confidence that you can detect whether or not it works. You'll likely have better ideas as you gain tests and experience and it's okay to revisit the test suite later on to make it easier to use and to understand.

    The nice part about this system is that it adds tests where you need them where the code is changing, whether it's a part full of bugs or a part under continual development.

    Keep in mind that to do testing this way, you need to be able to work in short, clearly-defined, and frequently-integrated steps (story and task cards, in XP terms). You also need the freedom to change necessary sections of the code (collective code ownership). It helps to have a good set of testing tools, so, depending on your language, there's probably an xUnit framework with your name on it. Also, it can be counterproductive to express your development and testing time estimates separately. At first, testing well will slow you down. It's tempting to throw it out altogether as a time sink. As you learn and your test suite grows, however, the investment will pay off immensely.

    Your goal is difficult but doable. It's well worth your time.

  4. The CCCC test method (Clicky clikcky clicka click) by fluor2 · · Score: 2, Interesting

    I would like to introduce my own method: The CCCC test method (Clicky clikcky clicka click).

    1. Open the application
    2. Click at an totally unexpected object
    3. Fill in some text somewhere (if expected)
    4. Goto 2.

    I find most warnings and bugs here, as long as you have some good assert() in your code. It's best if you use the CCCC method really fast, and test for like 4 minutes every time.

  5. Re:The CCCC test method (Clicky clikcky clicka cli by Hanji · · Score: 4, Interesting

    I once did a little palm programming, and I remember the emulator had a mode where it would randomly click on various controls and enter text really quickly, as a way of stress-testing your app, testing it's ability to handle any combination of input and options without blowing up. I wonder if something like that would be useful if the world of typically much-more complex PC programs...

    --
    A Minesweeper clone that doesn't suck
  6. Tests tell you what the code does... by daniel_yokomiso · · Score: 3, Interesting

    Not what it doesn't.
    As a start write down test specs for all of your use cases, even if the specs aren't automatizable. Then find a tool to simulate the user (e.g. HttpUnit is very good to simulate web users) and try to turn the specs into functional tests. Ensure that your application works today.
    The next step is remodularizing your code, try to find a tool that traces dependency diagrams in your code (e.g. Compuware's Pasta is an excelent free tool for Java). Module interdependency is a strong smell of bugs, so refactor them to make the dependencies acyclic, running your tests to keep everything under control.
    Then try to write unit-tests for the modules, create mock objects from them and check if they do what they're supposed to do. Repeat the second step for your classes (or data structures and functions if it isn't OO). Try to make all dependencies acyclic and create unittests for them.
    And during all these steps use Design by Contract to write down *all your assumptions*. Leave them on production code too (unless it's strictly necessary for performance). That way if your code breaks your assumptions the contracts will tell you. Also it'll force you to rewrite some code to make it checkable (i.e. exposing invariant predicates).
    Finally don't forget to check the common XP areas (extreme programming in Yahoo! Groups and news://comp.software.extreme-programming).
    It'll be no piece of cake but when you start to see a better factored code that keeps the bar green you'll be rewarded.

    --
    Disclaimer: If I disagree with you I'm probably trolling...
  7. Good luck, it's tough! by Anonymous Coward · · Score: 5, Insightful

    I have a similar situation, I have a bunch of code that "mostly works" and I'd love to have unit and acceptance tests.

    But it's really hard to add it later. I mean REALLY HARD. The tests are tedious and boring and after 2-3 I get tired and the tests have errors.

    If you follow the XP test-first technique, you code comes out MUCH different. You have low coupling, you have "testable" code where the pieces are interchangeable (so you can easily use mock printer objects or non-RDBMS backends, etc), and generally it's really elegant code with little extra work.

    And you don't get bored writing test-first because every time you write a test, you then write the code that passes the test and it's really a feeling of accomplishment! And you don't get "lost in the big picture" because you are focusing only on passing that one little test.

    The same is true for acceptance tests. I use HttpUnit to automate web apps, and although I'm not quite as religious about testing the interface, it's great for "add record, query record, delete record" stuff, to make sure it doesn't blow up when the end-user does something basic. For instance I had some code once that worked wonderfully, except login was broken. Since I was testing while logged in and never thought to log out and log back in, I never caught it in my manual tests. Automated tests can catch the stuff you forget.

    So I'd recommend requiring tests on all NEW code (you'll see a big difference between the old and new code I bet, in terms of simplicity and low coupling).

    And whenever you refactor the old code, start by writing tests that the old code passes.

    But it will really be tough to retrofit ALL your old code with tests. I'd even say it's not worth it because your tests will not be good.

    And remember: EVERY LINE OF CODE MUST EXIST TO PASS A TEST. That should be your goal on new code.

  8. If it is Java... by Not_Wiggins · · Score: 3, Informative


    One word: JUnit

    --
    Diplomacy is the art of saying, "Nice doggie!" until you can find a rock.
    1. Re:If it is Java... by Anonymous Coward · · Score: 0

      JUnit is not panacea at all. It is very limited.
      At least you also MockObjects.

  9. Retrofitting by jdybnis · · Score: 5, Informative

    This is something I've wrestled with too. Start where you'll get the most bang for your buck. Start with regression tests. I assume you're doing *some* testing (or at least your users are ;). When a problem shows up, make an automated regression test that surfaces that bug. Run it often and make sure the bug stays fixed.

    With a 300KLOC codebase I have to ask is it boken down into components that can be tested in isolation. If it is, congradulations you've done some good software architecture. You can start by testing the interfaces to the components. Make a test that triggers each error condition from each interface function/object. The tests will seem braindead simple (like passing in a NULL when a valid pointer is expected), but these sort of tests are suprisingly useful. Infrequently exercised error checking is one of the easiest things to let slip through the cracks when modifing an implementation. That will be enough to get your test framwork set up, and shake out all the forgotten dependencies between your components. Then it will be straightforward to add more testing.

    It won't be easy. You should expect you'll have to modify your code to make it testable. But if you expect to keep this code around for a while, it will pay off in the long run.

    1. Re:Retrofitting by drinkypoo · · Score: 1

      You have to be careful with your acronyms. I read that as 300,000 libraries of congress.

      --
      "You're right," Fisheye says. "I should have set it on 'whip' or 'chop.'"
  10. Step by step by pong · · Score: 4, Interesting

    1) *Everytime* you discover a bug from now on, write a test case that exposes it. Then fix it.

    2) Write new functionality test first. You are not allowed to implement new features unless you first implement a test that fails. Once in runs you are either done, or you got ahead of yourself and need to get back to writing a few more tests :-)

    1. Re:Step by step by m0smithslash · · Score: 1
      You need to consider your development process like a chess game. You cannot go back and change the moves that don't make sense anymore. Instead, consider where you are now and move forward.

      Instead of trying to go back and write test cases for everything, do as pong suggested and move forward from where you are today. Since the code is already working there is no real need to write test cases for it. Doing so would be contrary to the philosphy of XP that you only do what is necessary to complete the project.

      As you go on writing test cases for bugs and new features, you will naturally start writing test cases to cover already working code. Sometimes these will be indirect tests as you write tests for new code that depends on old code. Othertimes, new test cases will be needed for old code when a change to one piece breaks another piece. Again, don't worry about "what might be", only worry about what is. What might be often becomes what never was.

      90% of success is just getting started. Start simple and move on. I am sure someone famous said something like that.

      --
      Your friend and well-wisher
      m0smithslash
      http://www.ferociousflirting.com
    2. Re:Step by step by pruneau · · Score: 1
      Disclaimer: I'm not a XP specialist, just yet another of those corporate guy that managed to introduce some XP practices (ie _automated_ testing) at the class level and at the component level.

      Yes, adding a test for each bugfix, that's good for new code/patches. And should be absolutely followed.

      However, I'm under the impression our good friend Mr Pleonastic is after some way to apply testing (whatever the scope of it) to legacy code.
      Our experience there is that, even for code recently written, trying to retrofit even class/function-level testing is a royal pain in whatever you use for sitting.
      Because one of the major advantage introduced by good basic testing practice are cleaner interfaces and/or better functionnal separation.
      If this is wrong in the design, then find time to write code for the highest level of abstraction you easily can write test for.

      Do not forget as well that when you start automating test, which again is a "good thing (TM)", do not forget that you will have to maintain your test code base as well. Include that in your schedule.
      Last advice: an good automated test is unforgiving. What will happen most of the time when a test will break is because the test itself is wrong. This is usefull to asses modification just introduced.

      --
      [Pruneau /\o^O/\ warranty void if this .sig is removed]
  11. Re:Retrofitting is Hard! Refocusing is Easier! by Anonymous Coward · · Score: 1, Funny

    Hold on dude! You are not a programmer, so you shouldn't give advice on programming.

    Your resume clearly indicates that you are just a Perl hacker. As everyone knows, Perl is not a real programming language.

    Come back when you know a real programming language.

    Thank you and good night.

  12. Re:The CCCC test method (Clicky clikcky clicka cli by cpeterso · · Score: 2, Informative


    yes, these automated test tools are called "smart monkeys".

    "James Tierney, former Director of Testing at Microsoft has reported in internal presentations that some Microsoft applications groups have found 10-20% of the bugs in their projects using monkey test tools."

  13. Re:The CCCC test method (Clicky clikcky clicka cli by Anonymous Coward · · Score: 0

    I've done this kind of testing with an assembler I once ported. I used a program (from usenet) that measured the occurance of each possible multiletter sequence and generated random output text with the same ratios. Feed it English you get something the looks like English. I feed it assembly and it output something that looked like assembly. I rewrote the parser to reset when it got too deep and let it run. It took me over a week to fix all the bugs this method found. Most pf the bugs were in opcodes that no one every really used, but it was fun to clean up the code this way.

  14. Fuzzers by itwerx · · Score: 2, Informative

    Get what the QA people call a "fuzzer".
    There's two general types (often bundled together with a few other things as a test suite).
    The first generates random keystrokes and the second generates random data either completely randomly or following some set of guidelines (field length etc.)
    It still won't do everything that exposure to the real world will, but it'll get you a lot closer!

    1. Re:Fuzzers by Anonymous Coward · · Score: 0

      That's for Test-Last.

      The better pattern: Start trying to make sure for each bug you fix you can add a test on the bug's home function that only fails if the bug's alive and only passes if you kill it.

    2. Re:Fuzzers by itwerx · · Score: 1

      Good point!
      I guess I got a little past the scope of his question. Mod me Offtopic... :)
      But since you seem to know the process, any recommendations on Open Source tools he could use?

  15. redirect by bons · · Score: 2, Informative

    You're more likely to get people who know XP and TDD on http://groups.yahoo.com/group/extremeprogramming than you are on Slashdot, simply because the yahoo group is focused on that very topic.

    1. Re:redirect by russh347 · · Score: 2, Informative

      Another list that might bear fruit is the WELC list. a.k.a. Working Effectively with Legacy Code.

  16. XP Recommends by Anonymous Coward · · Score: 3, Informative

    What several posters have already suggested.

    Don't write tests for existing code just because it's there. Write tests for any code you have to change, and then do the change test-first.

    Initially, it's going to be hard because legacy code is usually highly coupled. If you pay attention to reducing coupling, over time your code base will start to improve.

    And get one of the xUnit clones for unit tests, and FIT (fit.c2.com) for acceptance tests.

    John Roth

  17. Refactor to testable code by Chris+Hanson · · Score: 2, Insightful

    If there's something that's particularly hairy in your existing codebase, the next time you need to modify it, spend just a little time refactoring it first so you can make your modification more easily. You don't need to do a six-month rewrite, just a little bit of refactoring to remove local duplication and confusion. And remember this about refactoring: It's not just about improving the design of existing code by making careful transformations of it, it's about rigorous removal of duplication.

    Write unit tests for the code you're refactoring immediately before you do so, in order to verify that your refactoring isn't actually changing the functionality. In other words, if you're extracting a method Foo::ExtractedMethod from the method Foo::BigMethod, start by writing a couple unit tests for Foo::BigMethod and verify that they pass. Then write a unit test for Foo::ExtractedMethod and watch it fail (because you haven't done the extraction yet). Then actually perform the extraction.

    Once you've added a new feature, don't consider it completed until you've done another small round of refactoring to remove any duplication that adding the feature introduced to your codebase. Do the same thing as above, writing unit tests for any code that you're refactoring that doesn't have any yet, and ensuring that your new feature's tests all continue to pass. After a few feature additions your codebase should actually start getting cleaner and your test coverage should go way up.

    Another tip: Try not to have interface-layer (View in MVC termonology) code do too much work. Try to keep the application's business logic in the Model layer and its interaction logic as much as possible in the Controller layer. This will make it much easier to write unit tests; your View layer will be very thin and mostly serve as a wiring-up of your Controller layer to various human interface widgets. The end result is that you can then write a test to validate that entering one value here and another value there doesn't invalidate your assumptions simply by tickling the appropriate Controller-layer code, rather than by trying to "fill in" text fields and "push buttons" via test code.

    If you currently have a lot of logic built into your human interface widgets, those are prime candidates for refactoring the next time you need to touch them (or notice they contain some duplication relative to the code that you're currently touching).

    And as others have said, check out the Extreme Programming mailing list at Yahoo! Groups. It's a great list with a lot of great people who are very willing to answer questions and help any way they can.

  18. Unit testing vs. integration testing by The+Mayor · · Score: 1

    Here are some of my observations having applied many forms of testing.

    One of the chief goals of XP is that units can, and should, be redesigned whenever any programmer identifies a unit that is poorly designed (contrast this to RUP, which is far less ad-hoc but still relies upon unit testing). This requires that your code be designed as components, and that each component have a well-defined contract.

    Based upon the brief bit you mentioned in your story, it sounds like integration testing is more applicable than unit testing. 300,000 lines of functionality (whatever that means...I follow Mark Twain's concept of "if I had more time I would have written a smaller program") written by smart but conscientious hackers sounds to me like design that does not meet the criteria of small components with well-designed contracts. One developer likely cannot redesign a portion of the program without it having undesirable side effects in different parts of the program. Of course, I am making assumptions about your code that may be incorrect, but you have given scant little regarding the project itself (what language was it in? is it a web app? a server app? a client app?)

    If your code meets the requirements for unit testing, write small, simple tests that should require little in the way of debugging (very small--most of my testing methods consist of only a few lines each). Test the contracts defined by each component. Build unit tests that test 3 things: successful conditions (maybe even repeat the test 100s of times to simulate load...code often behaves differently as buffers/queues fill up), errant conditions (to ensure things behave as expected), and boundary conditions (to ensure the contracts are met). My favorite unit testing tool is JUnit (I'm a Java guy).

    If your code isn't well suited towards unit testing, perhaps integration testing is more appropriate. This won't give you the XP capabilities of allowing complete redesign of components without affecting the system as a whole, but it can lead to more reliable code releases going forward. Furthermore, I find it helpful to build an integration test for each bug that surfaces, so that I don't reintroduce the same bugs I've previously fixed. In my experience, this works better than unit testing when writing tests for existing code. HttpUnit is great for integration testing web applications. There are many off-the-shell commercial apps to integration test GUI applications, too. Many of these are point-and-click. They basically record macros of mouse/keyboard events that can be replayed. Build up a large enough suite of these and you can regression test code, even if it was not designed well as encapsulated components. However, don't kid yourself--this isn't XP-like testing. These tests won't help you rewrite small units of your code. But they will help you test the functionality of the entire system when changes are made. That is not XP coding. XP really requires component-based design.

    Good luck! But don't kid yourself--XP cannot be bootstrapped onto code developed without using proper design principles. Testing is still very useful...but this testing is not in the spirit of XP.

    --
    --Be human.
    1. Re:Unit testing vs. integration testing by Anonymous Coward · · Score: 0

      I think you meant Blaise Pascal, not Mark Twain.

      "I have only made this [letter] longer because I have not had the time to make it shorter."
      -- Blaise Pascal (1623-1662). Lettres Provinciales, letter 16 (1657).

  19. Re:The CCCC test method (Clicky clikcky clicka cli by Anonymous Coward · · Score: 0

    There must have been so many within the 10-20% that they thought they were done.

  20. Start with removing unused/duplicated code... by tcopeland · · Score: 4, Informative

    > 300,000 lines of functionality

    If you have this much code, I bet there's some duplicated code in there. Ferret it out with CPD and you'll have that much less code to write tests for.

    It probably wouldn't hurt to search for unused code while you're at it - again, you'll reduce the amount of code you need to write tests for.

    1. Re:Start with removing unused/duplicated code... by Mr+Pleonastic · · Score: 1

      >If you have this much code, I bet there's some duplicated code in there. This is the second time in the thread (which is very informative, thank you everyone, I love you all, mwa) that someone has said that if I have 300K lines of code (it's Java), there's probably something wrong. This is a non-sequitur to me, and reinforces something I've wondered about for a long time-- do people just write tiny programs out there?? It reminds me of the time I was working on a different project using C++ Builder-- after the system was complex enough to have about 300 classes, many of them involving a lot of templates, the linker kept seg faulting. Their tech support asked me how many classes I was trying to link, and when I said 300, he just laughed at me incredulously. I don't think 300 classes is a lot to expect a tool to handle. The prorgram was about 1/3 done at that point. Our system has 1700 classes and a feature set so rich that it's sometimes hard to believe it's *only* 300K lines. For the record, we refactor all the time, and we do rapidly develop "stories" quickly trotted out for customer approval or the garbage can, and there's no "this is my code and my domain" mentality here, so we're doing a good bit of XP-ish stuff already. Anyway, I'm not trying to get into a macho My-program-is-bigger-than-yours thang, but I do sometimes wonder how pragmatically useful some methodologies and tools are once you get beyond web sites with a shopping cart bean that you have 7 people working on. Sorry! Venting! Continue with useful comments.

    2. Re:Start with removing unused/duplicated code... by tcopeland · · Score: 1

      > if I have 300K lines of code (it's Java),
      > there's probably something wrong

      Sorry, didn't mean to imply that. What I meant was that 300K LOC is such a large amount of code that probably some duplication will have crept in there despite your best efforts.

      I've worked on a system that had about 450K LOC and there was a ton of unused code/duplicated code in there - mostly because folks were afraid to clean it up due to a lack of unit tests.

      > I don't think 300 classes is a lot

      I certainly agree.

      > so we're doing a good bit of XP-ish
      > stuff already.

      Cool.

      > Sorry! Venting!

      Not at all... and again, I feel tools like PMD and CPD are useful for almost any size program - but most useful for very large ones where it's quite difficult to keep an eye on all the code.

  21. Re:The CCCC test method (Clicky clikcky clicka cli by necio_online · · Score: 1

    Some applications are just way too complex. As your application grows, this test will be less useful. I am trying to learn automated testing of web applications, and so far, I've found that javascript and popups are evil.

    This post is kinda silly.

    --
    say NO to software patents
    Donald Knuth Letter to the Patent Office.
    Carta a la Oficina de Patentes por el Profesor Donald Knuth

    --
    http://arhuaco.org/
  22. Debugging test code by angel'o'sphere · · Score: 1

    If you spend more time debugging test code than debugging your functionallity ... then your API/abstraction of the desired functionallity is wrong.

    For further development try this:
    Use Use Cases in textual form, probably "essential use cases", you can googel for that, is the easyst way in case you are not familiar with use cases so far.

    Further use "scenarios" for defining certain things which can be done from the outside with your application. A scenario is corresponding to a use case in that way that it offers different pathes of interaction through the same screens with differnt data and descissions of the actor(user).

    E.G: if you have a use case for an automated teller machine
    Use case: withdraw money
    Actor: customer
    1) insert bank card
    2) system asks for identification (4 digit code)
    3) enter code
    4) check code (E1)
    5) system askes for amount of money to withdraw .... .... and so on
    E1) report wrror: wrong code. If tries > 3 abort (continue at E2) otherwise continue at 2)
    E2) ...

    Thats a use case ... very simple set up. You have at least 2 scenarios so far, one where the customer gives the correct identification code and one(or more) where he does not. If you elaborate the use case you find also others, e.g. when the account has not enough money to withdraw etc.

    So: the above you likely do allready somehow (I asume you dont programm with a 400 page word document as "specification" do you?)

    Now FIRST: transform the scenarios into test code. Write Code that tries to perform the steps a customer needs to do to withdraw money. Call non existing methods/functions with that code. Write the code until it compiles! In C like language it won't run, because it won't link ... in Java, Python, Smalltalk, you can even run the code but get errors, ERROR means: TEST FAILED.

    Now start to connect the test to your code, by either writing non existing code or by building a bridge from the test driver to the existing code.

    My intention is that the test code is so simple, that it performs one action, a user would do, with one line of code.

    If you follow that idea, your API to your backend will get very clear and simple. If you have a simple API, you likely have simple code behind that API. When you have simple code you liklely have less bugs.

    And with a test case derived from a customer scenario you have a test case which shows:
    a) broken functionallity after code reworks
    b) what the system is intended to do -- it becomes specification!

    Sample:
    class TestWithdrawelWithCorrectCodeAndEnoughMoney { // this is one scenario from the use case "withdraw Money"
    String customerName = "Billy Joe";
    String accountNumber = "123456789";
    String code="1234";
    double amount = 100.00;

    MyApplication system = new ...(); // likely you have a factory for an Facade object or something

    public void testScenario() throws Exception {
    system.enterCard(customerName, accountNumber); //ignore: 2) system asks for identification (4 digit code)
    system.enterCode(code); // system checks, with result ok 4) check code (E1) // ignore: 5) system askes for amount of money to withdraw
    double oldBalance = system.getBalance();
    double money = system.withdraw(amount);
    if (money != amount) throw new WithdrawErrorException("wrong amount withdrawn");
    double newBalance = system.getBalance();
    if (balance + amount != oldBalance) throw new WithdrawErrorException("wrong balance after withdrawel");
    }
    static void main(String[] args) {
    try {
    testScenario();
    } catch (Exception x) { // test failed, print it or something
    }
    }
    }

    As you see, the code in testScenario() is simple, and besides the fact that it is

    --
    Cost free eBook I read (by iBook/Kobo/Amazon/ObookO/Gutenberg etc.): "The Green Odyssey" by Philip Jose Farmer.
  23. Automated testing by Anonymous+Brave+Guy · · Score: 1

    As others have noted, retrofitting some sort of automated testing would be very hard, but can be well worth it in the long run, maybe even this late in a development.

    The key ingredients of successful automated test suites I've seen are...

    • Some sort of macro language that:
      • describes the inputs your program accepts (from a UI or otherwise)
      • is recordable by your program
      • is replayable by your program;
    • Some sort of state record that can be:
      • generated at any given point in your program by a macro
      • checked against when replaying that macro.

    Given the above, all you need is a set of macros and a record of the state that should result, perhaps generated by that macro. (You probably want to record some sort of summary of the state that's likely to reflect as small a change in your state as possible, rather than the full state; often it's the fact that something has changed that is important, not the specifics of what the change was.) Then just replay all the macros and compare the state produced by your current program with the recorded, known-good states, and investigate any discrepancies.

    Obviously writing an engine to do these things can be a very significant amount of work. For UI code, particularly if you're using a heavily message-based architecture, it's probably easiest to put the macros in at the message level, IME, which at least gives you a relatively easy way to replay those macros later.

    --
    If you disagree, post your argument. (-1, Overrated) isn't your personal censorship tool for views you don't like.
  24. Evolving to testability by joel.neely · · Score: 1

    In addition to the other good comments already posted (especially those regarding refactoring), let me suggest making increased testability part of the ongoing maintenance process. Specifically, every time a problem is reported, make the creation of a test that exposes the error the first order of business. (Of course, make it as fine-grained as possible, and refactor as necessary to achieve that focus.)
    Then use that test to guide the resolution of the problem. Iterate throughout the life of the project.

  25. Moo by Chacham · · Score: 1

    Write a test document first.

    The biggest problem with tersting tis that people test what the code was programmed to do. However, testing should be based on the requirements, to make sure that it does what it was intended for. Also, if the test document is written before coding, it means people won't cut corners when writing that document (due to optimism) and it may help drive coders to code the really important parts first.

  26. Blackbox testing by gtshafted · · Score: 2, Informative
    Try Jakarta's JMeter.

    Instead of having to write tests for HttpUnit, you can simply record tests with JMeter. You also have the added benefit of JMeter's load testing capabilities. The only downside is that JMeter's UI isn't very mature (or intuitive for that mature).

  27. Makes assumptions explicit by nimblebrain · · Score: 2, Insightful

    One thing unit testing is often spectacularly good at is pointing out where assumptions have been made and not spelled out. This often takes the form of "negative testing". (What happens if you add a NULL to that list? What happens if you try to access the -1th element in an array? What happens if you neglect to set an IP address?)

    What you'll likely find is that, in a number of spots, the refusal or assertion is not spelled out. Occasionally, it can take some mulling to figure out how to deal with those edge cases (could NULL be valid in some circumstances? Should we make a different derivative or member variable to determine that behavior?)

    There's much positive testing to do, too.

    Don't bother with testing the extremely simple stuff. If it's to the point where you might as well be questioning whether the compiler can do its job, you won't gain value from it, and it will bore you to tears. (Mind you, if you have one of "those" compilers whose very foundations you question... :)

    If you have classes that produce output lists/objects, one nice technique to use is, instead of checking the output manually, is to create an equals/== method for your output, then create the expected output in your test and compare it (via your equals) with the output from the class.

    Some other miscellaneous observations I've made:

    • Unit testing gets a lot more interesting and a lot easier the first time it flags something real. Fortunately, it often happens just as unit testing was becoming boring again.
    • Always throw in a negative check (something designed to produce the wrong output) - it's easy to produce checks that always return true even if the output is wrong (e.g. I had a list comparison function to determine equality which would kick out if one of the lists was shorter, but the elements were equal up to that point - doh!)
    • Isolate the class as much as you possibly can. There's a whole technique to creating "mock objects" to help. For example, instead of using a real database object, make a fake one that returns specific rows, or instead of creating something that listens for a hardware signal, create one that waits for a command from the test.
    • If you think of some condition your class might violate, avoid the temptation to go off and fix the class first. (At the very least for motivation's sake so you can see a failed test - seriously!) This is also a good thing to do if you're having a debugging session and one of those so-called "impossible!" conditions is happening (this could never be negative! there should always be something on the command line!).
    • If there's an illegal condition you want to test for, add an assertion/exception into your class instead of merely checking the result. This helps 'fail' other tests and code that aren't setting up objects properly but report that they 'pass'.

    One of the hardest things about writing unit tests is trying to interface to the outside world. Whenever you can, avoid it. You can fake things to a point (using 127.0.0.1 as an IP address in some tests, for example), but you'll have to fall back on functional testing at some point. That's another good reason for keeping as much logic out of the view as you can.

    One note of hope: the most difficult part of unit testing is getting started. Honestly. Once you "get it", you will always "get it", so hang in there :)

    --
    Binary geeks can count to 1,023 on their fingers :)
  28. The test suite I use, and how I use XP by SKarg · · Score: 3, Interesting

    I ran across an article a couple of years ago by Chuck Allison in C/C++ Users Journal about the The Simplest Automated Unit Test Framework That Could Possibly Work. It included test frameworks written in C, C++,and Java and opened my eyes to doing best practices to the extreme. It also showed me how I could apply unit testing to my C code. You can download free Test Frameworks (Test Suites) for other languages.

    Unit testing was the first XP key practice that I started to use. When I would have to make a change in my mature code, I would add a unit test section to the module I was changing (using #define TEST), and add a main() to execute the unit test (using #define TEST_MODULENAME). See examples of this on my software page. I then began using test-first programming by writing the unit test first, seeing it fail, then writing just enough code to make it pass.

    Other extreme programming sites that have been useful have been extremeprogramming.org , which has a great tutorial that includes an introduction and overview, and the site Extreme Programming.

  29. Refactoring, step by step by dna_(c)(tm)(r) · · Score: 2, Interesting
    Well, from the short description you gave, it seems to me you have some architectural/design problems.

    I would recomend some reading: first Refactoring which talks about code "smells".

    One such smell is "Inappropriate Inimacy"

    [classes]spend far too much time delving in each others' private parts
    recipes are given to resolve these kind of problems. The nice thing about it, is that it talks of symptoms of problematic code, you will more easily understand where your problems occur and why.

    Next, read a good book on unit testing: Unit Testing In Java

    At some point, you might get interested in Design Patterns a good alternative for Java with more practical examples.

    Don't try to add all tests at once, do it on a regular basis, one by one, as needed to support your refactorings, and for regression testing. You don't want to find the same bug more than once.

    Start using Ant as a build tool, automaticaly executing your test suites at least with every build.

    Try to achieve an MVC (Model - View - Controler) architecture, the View (awt/swing, html/jsp, console,...) should only be responsible for showing the state of the model. The Controler should only validate user input and manipulate the Model (Mediator pattern). The Model should represent the business logic (you'll typically will find Person, PersistenceService, AuthenticationService objects here).

    Just don't rush things, the goal is not to make your code compliant with The Only Right Way Of Doing Things(TM) the goal is improving your code for achieving more robustness and maintainability.

    BTW, I'm speaking from experience, as Sr developer/analyst/architect in a small team and as coach/instructor. I wish you succes ;-)

    1. Re:Refactoring, step by step by DukeyToo · · Score: 1

      Talking about books, I recently bought Test Driven Development by Kent Beck (XP proponent), and have not regretted it. If you need some help on learning to write unit tests, it has some really basic examples to help get you started, and is written in a very down-to-earth style.

      Other than that, I would add that writing tests is HARD, at least at first. It takes some getting used to, and you will look back on your first attempts and laugh/cry some day.

      The best thing to do is to stop talking, and start doing. Find yourself a test framework that you can live with (JUnit or derivatives are nice) Write tests to reproduce bugs, and then fix the bugs so that the tests pass.

      You can also consider something called "Design By Contract". This is a way of making assertions within the code itself; an explicit documentation and validation of a method's input and output. Personally I found this to sound better than it was, but that may have been something lacking in my execution.

      --
      Most writers regard truth as their most valuable possession, and therefore are most economical in its use - Mark Twain
  30. Re:The CCCC test method (Clicky clikcky clicka cli by ForestGrump · · Score: 1

    Working on a project right now.
    I consist of the entire QA team.
    and that is my testing method.
    play with the features until I go insane.

    -Grumpy old tester.

    --
    Is it true that more people vote for the winner of American Idol, than vote for the president? -Ali G.
  31. There are other ways... by ebbe11 · · Score: 1

    ...Such as this

    --

    My opinion? See above.
  32. Retro-fitting can be hard by PinglePongle · · Score: 1

    Like a previous post, I'd suggest "evolving" to testability - every time you fix a bug or add a feature, do it test first.
    You will have to spend some time setting up the testing framework - a structure for your unit tests, the "non-code" stuff, and a way of finding out asap that you've broken a test.
    Depending on your environment, you could use something like AntHill or CruiseControl to automatically run all your unit tests as part of a (timed) build process, and email the results. CruiseControl also allows you to specify regular intervals at which your entire code-base is checked out of source code control, and unit-tested - you get an email if something breaks.
    A key problem for most systems is getting all the "non-code" stuff (in my case mostly databases etc.) into a known-good state so you can rely on a unit test reporting accurate errors when trying to insert a duplicate value or delete a non-existing record - again, automate using (something like) Ant - Ant will do a lot of this stuff for you on non-java projects too.
    Once you have refactored sufficiently, you can hopefully start testing independently from the "non-code" items.
    I'd suggest buying Kent Beck's book "Test Driven Development" for more ideas on how to code in this paradigm - it's very very good.
    Another book worth reading is The Pragmatic Programmer (they have a website too). Especially the "no broken windows" section is very worthwhile...

    --
    It's all very well in practice, but it will never work in theory.