Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

  • Edit and pray - analyze thoroughly beforehand. minimize changes. Use functional testing: test, test, and retest afterward, test cross-cutting parts of the system afterward. Gives the impression that is a very cautious technique, but is it effective?
  • Cover and modify - Give yourself a safety net by adding quality unit tests around the section of code being modified. We will know instantly if we have broken known good behavior. ("code that bites back")

Software Vise

vise (n.). A clamping device, usually consisting of two jaws closed or opened by a screw or lever, used in carpentry or metalworking to hold a piece in position. The American Heritage Dictionary of the English Language, Fourth Edition

When we have tests that detect change, it is like having a vise around our code. The behavior of the code is fixed in place. When we make changes, we can know that we are changing only one piece of behavior at a time. In short, we're in control of our work.

...

Unit tests are great, but there is a place for higher-level tests, tests that cover scenarios and interactions in an application. Higher-level tests can be used to pin down behavior for a set of classes at a time. When you are able to do that, often you can write tests for the individual classes more easily.

Developing tests for legacy code

Problems that can arise trying to put legacy code into a test harness. We are trying to create tests that run fast and don't have side effects.

  • Dependency on external systems, data models, etc. are baked into the code we need to modify - the logic we need to test hasn't be sufficinetly abstracted.
  • We can't physically instantiate a particular class in our testing harness:
    • It tries to pull in external libraries and APIs that can't run in the testing harness.
    • Constructing of the class requires passing objects we can't create. (an "irritating parameter").. Examples: DB Connection, network socket, etc.
    • The code we need to test is tied directly to event handlers in GUI or other UI code that cannot be executed independent of user action.

Dependency is one of the most critical problems in software development. Much legacy code work involves breaking dependencies so that change can be easier.

The Legacy Code Dilemma

When we change code, we should have tests in place. To put tests in place, we often have to change code.