Skip to content

Refactoring to Metaphors: Wiring Harnesses

Any fool can write code that a computer can understand. Good programmers write code that humans can understand. -Martin Fowler

I want to write testable code, but how do I get started?

If you have trouble starting to write testable code, you’re not alone. So many of the concepts are written about by brilliant coders (like Michael Feathers and Joshua Kerievsky), but they tend to write for other advanced developers looking to improve their work. If you’re just trying to get started down the road of testability, reusable code, and reliable changes, it can be daunting.

So, I’m writing this post to help you take those first steps. I’m hoping this gives you a simple metaphor you can use to help think about making code testable and reusable. It’s not a silver bullet; this is just one perspective on one technique for writing testable code. And it doesn’t even start with tests! And, like any tool, you can use it wrong.

Wiring Harnesses

A wiring harness is nothing more complicated than a group of wires (ins and outs) bundled together by some kind of connector, tape, insulation, or ties.

All the operations in a piece of code – a method, an object, a module – can be seen as a series of inputs and outputs.

(There is a third kind of code, which I just call “neither” for purposes of this metaphor. It’s just anything that isn’t an input or an output.)

Quick contrived example:

Imagine the code block as a plastic connector. The inputs are wires coming from one end of the block, and outputs are wires coming out the other side.

Refactoring Example

Trying to Test This Method

What is this method responsible for knowing?

1. How user searches are sent to the server ($_GET)
2. Where author objects are stored (What is it, maybe Redis?)
3. The storage API implementation to retrieve author objects
4. Where articles are stored (Mysql this time?)
5. The storage API implementation to search articles
6. The storage API implementation to retrieve articles
7. How to populate this particular View
8. How to render a View

There are also some especially tricky spots here, especially in PHP-Land:

1. \Cache::get() and \DB::find() are static methods, and really complicated to mock or stub.
2. new \View() is also really difficult to mock or stub, and generally has to be replaced with Dependency Injection (or refactoring!).

What will we have to Mock or Stub or Inject to get this under test?

1. \Cache::get($id) (good luck in PHP)
2. \DB::find('articles') (ouch)
3. \DB->where()
4. \DB->get_all
5. new \View($view) (another hard one)
6. \View->render()

This is too much! Having to set up and tear down this much architecture to test such small functionality is crazy. But with a little heuristic refactoring, we can make the test match the size of the functionality.

BUT FIRST: A Public Service Announcement about $_GET safety

Using $_GET directly is dangerous. First, because it’s an array, you must always check for isset(). Second, because you never never trust user input!

Reorder to be basically input first, output last

This is a lot better – there are still some issues though. I still think of calling out to the DB as an output. It results in an input, but should be moved to its own “connector”.

Improve order by extracting get articles method

Improve order by extracting get/tick authors method

Are we seeing the bundling? Now the Author inputs are all wrapped up nicely, as are the Search results.

Let’s take it to the Xtreme

I like to extract the View logic as well

Now where are we?

images.duckduckgo-1

Great, you rearranged the code and took 40 lines instead of 16. Good for you.

But now we can test this method so easily!

Now, the method’s responsibilities are trivial:

1. Where to get the Author my View needs
2. Where to get the Articles my View needs
3. How to pass the Author and Articles to my View

There are only 3, and each is reflected in 1 line of code.

Not only that, but I would argue this method might not even *need* testing anymore.

Even if we did, what do we have to mock?

1. Mock $this->get_author_for_author_articles($id)
2. Mock $this->get_author_articles($id)
3. Spy on or inspect $this->get_author_view($author, $results)

Each new method we created also has limited responsibilities, and could be worked on and refactored further.

Conclusion

By using a simple metaphor for organizing our code, and a simple technique (Extract Method), we have refactored our method to be:

1. More testable
2. Less tightly coupled
3. More intentional

I hope this helps you develop an intuitive sense of what testable code can look like. This is just the beginning. Now go forth and test!

Published inUncategorized