Versions Compared

Key

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

...

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.

Sensing and separation

...

Why do we need to break dependencies in our code base in order to implement effective unit tests?

  1. To separate the code we want to put under unit test from other dependencies that make it impossible/extremely difficult to run under the test harness. Example: A web application bein being dependent on the Java servlets API makes it difficult to instantiate the servlets in our test environment without a web container.
  2. To sense the effects of our code on other components in the system. Using the same example of a web application, our tests are going to need to inject certian certain HTTP requests into the component under test and detect if certian certain responses are emitted. We might do this by creating a wrapper around our application that provides a simplified interface to substitute for HttpServletRequest and HttpServletResponse.

...

  • Fake objects can break the rules of good design. Use public properties and methods to make it easy to set and retrieve values from test code.
  • Fake objects are not as sophisticated as full-blown mock objects. Mocks provide a more complete simultation simulation of the object being substituted and have the built in ability to set assertions for acceptable interactions from the test code. Mocking frameworks exist for most OO languages and can be quite useful, however simple fake objects will be acceptable in most situations.

Seams

A seam is a place where you can alter behavior in your program without editing in that place.

Every seam has an enabling point, a place where you can make the decision to use one behavior or another.

Types of seams:

  • Preprocessing seams - use the macro facility built into the language to substitute in fake implementations of dependencies while under test and the real implementations in production. (i.e. in C/C++, #ifdef TESTING, etc.)
  • Link seams - substituting in alternate implementations relying upon the linker. The alternate implementation must use an identical interface. Can be done at compile time, i.e. as part of the -l options passed to the compiler in the Makefile for C or C++, or at run time i.e. by setting the Java classpath variable.
  • Object seams - the most powerful and cleanest seam available in OO languages. Allows us to substitute in a new implementation by creating a test class that inherits from the same same expected base class or implements the same interface. not all method calls or seams. A seam requires an enabling point, so a case where we create an object instance and make a method call within a single method is not a seam. However if we pass in the object to be operated on as a parameter, then the argument list for the method can be an enabling point.

A structured way to change legacy code

...