When people first start writing tests, they will notice how horrible code is structured for them.
In general, this is true most of the time, except when you write tests along with the code which is called Test Driven Development.
I personally think it's a thing that every developer goes through once they start to see the importance of tests. You start to see code differently, you start to see edge cases and dependency mingling.
In this article, we briefly talk about a common technique for breaking dependencies and finally about the general tooling used for refactoring.
The word seam comes from clothing, which means A line where two pieces of fabrics are stitched together.
A seam in software is a place where two parts of the code meet and where something else can be injected. The piece on each side only touches the other right at the seam.
How are Seams useful?
As stated above the biggest challenge in getting legacy code under tests is breaking dependencies.
If you look at the seams in software, you will find opportunities to break dependencies. If we can replace behavior at seams, we can selectively exclude dependencies in our tests.
We can also run other code where those dependencies were if we want to sense conditions in the code and write tests against those conditions. Often this work can help us get just enough tests in place to support more aggressive work.
This place in the code base is called an enabling point, a place where you can make the decision to use one behavior or another.
Different Kinds of Seams
There are three different kinds of seams:
- Preprocessing Seam – C and C++ provides with preprocessing tool. This preprocessing, a macro preprocessor runs before the compiler. We can use the preprocessing seams to replace calls to another independent piece of code.
- Linker Seam – In programming languages, when a source file contains an import statement, the compiler checks to see if the imported class really has been compiled. We import those classes from classpath environment variables for eg; JDK path. We can replace those imported class and alter their behavior during by using dynamic linking. This link seams, should be different for test and production environments.
- Object Seams – They are the most useful seams available in object-oriented programming languages. Object seams are mostly used to isolate the behavior while testing.
There are some common tools that you would want when working with legacy code.
The most obvious ones are an IDE (Integrated Development Environment) and your build system, but you also need a testing framework.
Refactoring without tools to aid you is fine but can be a huge hassle, that's why if it's possible then you should definitely use them.
Automated Refactoring Tools
As we have learned before, refactoring is the act of changing code without changing existing behavior.
To aid in refactoring, there are tools in our IDEs that help us make it easier.
But what do they do exactly?
They verify that a code change does not change behavior.
This is very useful because without it, you can introduce some subtle bugs.
- When you attempt to extract a method and give it the name of a method that already exists in that class, does it flag that as an error?
- What if it is the name of a method in a base class—does the tool detect that?
I'm fairly sure that you encountered these kinds of errors before because they are integrated into almost all IDEs.
PS. Before using an automated refactoring tool make sure that you have tests around your code because it's not always guaranteed that they will make the correct refactoring.
In our last article we talked about the importance of mocking.
To help us in that there are lots of different mocking object tools.
Feel free to check them out here.
Unit Testing Harness
A lot of people think that they can automate writing their tests or test through a GUI or a web application.
Unfortunately this is fantasy for now because most of these "paid" tools fail and debugging why they fail is hard.
The most effective testing tools are free.
One of the popular ones is called xUnit.
The xUnit is a small, powerful design for a unit-testing framework.
xUnit has been ported to multiple langauges such as:
- CppUnit (For C++)
- Junit (For Java)
- NUnit (For .NET)
The features of xUnit are:
- It lets programmers write tests in the language they are developing in.
- All tests run in isolation.
- Tests can be grouped into suites so that they can be run and rerun on demand.
General Test Harnesses
The xUnit frameworks are mostly used for unit testing. You can probably use them to test multiple classes at once but that sort of work is more suited to FIT and Fitnesse.
Framework for Integrated Tests (FIT)
FIT is a testing framework developed by Ward Cunningham. The idea of FIT is simple and powerful. If we write documents about our system in tables as inputs and outputs, then the FIT framework can run them as tests.
FIT accepts HTML, runs tests defined in HTML tables in it, and produces HTML output for test reports.
The power of FIT is its capability to foster communication between people who write software and people who need to specify what it should do.
FIT helps with acceptance testing.
You can read more about it here:
Fitnesse is essentially FIT hosted in a wiki. Most of it was developed by Robert Martin and Micah Martin.
Fitnesse supports hierarchical web pages that define FIT tests. Pages of test tables can be run individually or in suites, and a multitude of different options make collaboration easy across a team.
Like all of the other testing tools described in this chapter, it is free and supported by a community of developers.
We covered two chapters in this article because they were both very short in the book.
In the next chapter we will start talking about actual refactoring method so stay tuned for that.
Thanks for reading.