Posterous theme by Cory Watilo

Filed under: Testing

The 6 steps to mastering refactoring

Last night I was in the process of refactoring some of the code in the project I am currently working on. After a while I fired of the following update on twitter

Is refactoring more fun than writing the initial code? It's like putting the finishing details on a piece of art,see it take it's true shape

It did not take long before I got a couple of replies from people agreeing with me. This got me thinking. Refactoring is essentially the same as restructuring your code and to many that is a daunting task, something they would rather try to avoid because of the risk that something might break. I came to the conclusion that there are 6 things that I make sure I follow before and during any refactoring and here they are

  1. Make sure you have good suite of tests – Let me be perfectly honest here; if you do not have a good suite of tests in place for the code you are about to refactor, then it is not going to be a pleasant journey, period. Having a suite of tests you know prove that the code is working as intended is going to make you a lot more comfortable with making radical changes to it. They are your primary refactoring safety net, so make sure they are in place. You are going to want to run these tests a lot so take the time to setup keyboard shortcuts to re-running them, you are going to be thankful you did. Re-running your tests should not be a chore, it should be second nature and switching out of code-mode to move the mouse to select the menu option to run the tests are going to start gnawing on your patience pretty quick. If re-running your tests is tedious you will not run them as often as you should.
  2. Get to know the tools in your toolbox – Most modern development environments have support for the basic refactoring steps, such as renaming, extracting method, reordering parameters and so on. Refactoring can quickly become repetitive and tedious, but it does not have to if you know how to take advantage of the tools at hand. Learning the keyboard shortcuts to perform the basic refactoring steps is going to solve both of these. Take the time to learn the keyboard shortcuts to speed up the process and soon enough code will literally change shape as if magic was involved. Tools such as ReSharper, CodeRush and JustCode brings a lot to the table that and, once you know the tools, it will increase productivity a lot.
  3. Use a version control system – I can not stress this enough, but being able to reverse the changes you have made can, and will, turn out to be a life-saver more than once. By having your source code under version control you bring instant reversibility into the game and there is no need to worry if you find yourself in a place where the state of the code is horrible. Simply revert the changes you have made and start over from scratch, but with the experience gained and lessons learned from the previous failed attempt. The most widely used version control systems, such are Subversion, Git and Mercurial, are all free, so there are little or no reason not to use them, even if it is just a local repository on your hard drive.
  4. Keep the iterations small – Part of the agile process is to keep tasks small and the same goes for refactoring. Focus on improving one thing at a time or you are likely to loose track of your changes and end you reducing the quality of your code instead of increasing it. If you (and again, you really should) have your source code under version control, then make it a habit of (at a minimum) committing your changes after each iteration. Break down large tasks into many smaller ones to get a better overview of changed you are making.
  5. Have a game plan – Do not start refactoring your code without having a game plan. Just randomly trying to improve the quality of your code is to sell yourself short. Take a moment and get a mile-high view of your code, then formulate a game plan. During the initial coding, you probably made notes, mental or in some other form, of points that you would like to revisit later on to improve, start from there. Having a game plan enables you to define the confines of the iteration, in other words you will be able to know when your work is done.
  6. Do not add new behavior – Refactoring is about improving the quality of your code. If you find yourself introducing new behavior then immediately stop. Looking at the refactored code might make it more evident on how additional functionality could be introduced, than if you had looked at the old code. However if you decide to walk down that path you are no longer refactoring, you are now designing a feature and you should revert back to the normal process, such as TDD. The last thing you want to do is to change or add behavior in your code without making sure it is covered by tests.

There you have it, my steps on how to make refactoring into a pleasure instead of a burden. If you have any more steps that you follow, please leave a comment! I would love to hear about them.

So you write unit tests, but do you really TDD?

Many developers are claiming to be TDD practitioners, without really understanding the differences between Test-Driven Development and unit testing. Somewhere along the line the definition of TDD has gotten lost and it is time to set the record straight; just because you write unit tests does not mean you practice TDD.

Unit Tests

When doing unit testing you want to isolate the smallest possible unit of your application, usually a class, and test it in complete isolation. This means that you need to be aware of any external dependencies that the unit under test has, so that you can create an abstraction between the unit and the environment around it, usually by taking advantage of mocking framework or fake objects.


Without isolating the unit from its external dependencies you would be creating integration tests. An integration test asserts that the interaction between two or more units produces results that satisfy functional requirements. These types of tests are completely different from unit tests, but are equally important.


A unit tests should not be concerned with the internal implementation details of the unit under test. Testing implementation details such as internal members or state is a code smell and is almost certain to produces brittle tests; tests that break when you make seemingly unrelated changes. If you find that you keep having to rewrite your tests when you update the internal implementation of your units, then you are probably heading down a terrible path and should step back and reconsider the way you are working.


Instead a unit test should only be concerned with testing externally observable behavior of the unit. This does not only mean the public API of the unit, but also behavior that can be observed through the dependencies of the unit. What this means is that the unit under test might be calling out to one of its dependencies and that call can be observed from outside of the unit.


One thing that you need to tend to when you are writing unit tests is to make sure they have no side-effects. A unit test should run in complete isolation of each other and the order in which they are executed should not have an impact on whether they fail or not. This could happen if the units have not been isolated and one test alters the state of a dependency (for example a database) in such a way that another test will fail because of it.


I recommend that you read ”What is unit testing?” by Justin Etheredge if you want to learn more about the characteristics of a unit test.

Test-Driven Development

The one thing that TDD has going against it is its name. Because it has the word ‘test’ in the name it is often misunderstood as being a more fancy way of saying that you write unit tests for your code, nothing could be more wrong.


So exactly how does TDD differ from writing unit tests? Strictly speaking from a testing perspective, not much. You use the same techniques and tools to write your tests. Both tests are unit tests and both tests are interested in the externally observable behavior of the unit. The difference lays in the goal of the tests.


TDD is not about testing, it is about how you develop software. The goal of TDD is to produce code of high architectural quality by letting the tests drive the design of your code. This done by applying what is known as “Test First”, where you write the test case before you implement the code that will make it pass. This forces you to really think about how the implementation should work, instead of writing a test to make sure the code you already have in place works.


This is where the ‘test’ part of the name skews many people’s conception of TDD, because it is not really so much about testing as it is to write down (executable) specifications on how the code should behave. Got that? First you write down your specification on how it should work, then you write just enough code to make it work.


Because of the naming confusion people like Martin Fowler suggested it should be called Specification By Example and Brad Wilson (and friends) started calling it Design By Example, both being far more descriptive of the real intent of TDD.


When writing code using TDD you follow a what is known as the “TDD Mantra” also referred to as Red – Green – Refactor. This breaks down to a very simple development cycle.

  1. Write a test that captures the intent of the code
  2. Run the test to be sure it fails
  3. Implement the least amount of code required to make the test pass (do not take design into account)
  4. Run the test to make sure it passes
  5. Re-factor the code into a more elegant design
  6. Re-run tests to make sure the refactoring did not break anything

By starting with writing the test first you really have to think about the intent of the code you are going to implement. You run the test to make sure you do not get a false positive. Next you write just enough code that is required to make the test run and verify the implementation by running the test once more, making sure it does not fail this time. Once that is done, you refactor your code to a more elegant design, taking full advantage of all your code design skills and practices. After you have cleaned up your code it is a good idea to run all of you tests again to make sure you did not break anything else in the process.


If you stay true to this development cycle you will realize that you are no longer adding code based on assumptions, but instead only add the code that your specifications require. There are probably not a developer alive that is not guilty of adding that extra method overload or property just because it could turn out to be useful. Instead of being useful it could turn out to be a maintenance nightmare and because of backward compatibility issues you may not be able to remove it later on. Scott Bellware talks about this in his ”TDD is about not knowing” post.


Assumptions really do have a huge impact on code design even though we do not think much of it. Even if you were to follow the TDD development cycle you are still going to be putting a great deal of assumptions into your design. It is in our nature as humans and, most of all, developers. Keith Braithwaite created a workshop titled ”TDD as if you meant it” to challenge our perception of how many assumptions we really put into the design of our code.


Gojko Adzic attended Keith’s workshop and blogged about it in a post entitled ”Thought-provoking TDD exercise at the Software Craftsmanship conference” and later on Mark H. Needham did a ”coding dojo” on the same subject. I encourage you to visit and read those posts, even if you are a seasoned TDD practitioner, I’m sure they will challenge your perceptions.


Even though TDD is not explicitly about testing, the tests still have some quality assurance values. For example they are still a prime candidate for automated regression and acts like a safety net when you refactor your code to reach a higher degree of architectural quality. Be sure to treat the tests like first class citizen in your project and take the time to keep them up to date and clean. Avoid commenting out a failing test because you feel you are under time constraints or with the notion that you will be coming back to fix the test at a later stage.


The tests are your specifications. If your specifications are not up to date and correct then there is no way to tell if the code you are developing is the code you should be developing. You would not treat a formal specification document with such disregard would you? If not, then why would you want to do it with an executable version of them? They are your friend, they will keep you out of trouble and out of the debugger. They are the first client that will touch your code and, if you take the time to craft them well, they will never lie to you. Embrace them.

Summary

I hope that it is evident what the difference are between just writing unit tests and committing to the Test-Driving Development. There is very little required to get started with TDD, if you are already writing unit tests then you have all the tools you need to get going. TDD is a discipline and as time passes by you will use your gained experience to hone your skills. Why not grab a couple of friends and setup a ”Code Kata”, getting other peoples insight is awesome whether you are just fine tuning your own skills or just learning from scratch!


You can follow me on Twitter @ TheCodeJunkie