Help! I’m Terrible At Migrating/Restructuring Code In A Test-First Manner

I’ve spent the last day or so restructuring some code – specifically converting a WinForms form into a user control so that I can host the control in several different forms that need it. This involves changing the presenter for the original form, the form itself, the view interface for the form and then re-creating these as the user control.

I’ve noticed that when I do something like this I tend to hack at it from the implementation first and then go back and migrate the unit tests later (often commenting out unit tests as I’m going just to get the changes made first). I know intellectually that I should be migrating my code via test-first development efforts… but I seem to be lacking the knowledge to get that done correctly.

So, I wanted to ask: how do you go about migrating code like this? Do you take a test-first approach? If so, what tips and tricks can you share to help me learn this aspect of test-first development?

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs - the amazingly awesome podcast audio hosting service that everyone should be using, and where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in AntiPatterns, Continuous Improvement, Craftsmanship, Model-View-Presenter, Philosophy of Software, Pragmatism, Unit Testing. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • In a general sense I do something like this:

    1. Copy the tests to where they want to be
    2. Make those tests pass
    3. Remove the code from the old location
    4. Check that only the tests you expect to fail start failing
    5. Remove the old tests

  • Usually when I find myself unable to do test first it’s when I don’t have a clear idea of what I need to implement.

    Working in smaller steps helps. If I have a rough idea of what needs to happen, sometimes I can detail it out as a todo list in comments in the code, or on paper if I need to draw relationship or interaction diagrams. This detail can provide the guidance for the needed tests.

    When you don’t even have a rough idea of what the end product will look like, test first is very difficult and frustrating. In this case, I start by doing a wide width, shallow depth list of possible ideas. In other words, think of as many possible solutions without working out all the details. Focus on a variety of approaches, not depth of a single approach.

    Then I’ll pick the most promising and prototype without tests. This allows me to understand the problems and needs of the solution in greater detail. Sometimes the first try is fine, sometimes it isn’t and another approach is attempted.

    Whether I delete the code and start over test first or keep it and write tests afterwards is a judgement call.

  • jdn

    Instead of migrating unit tests, I’d create new specs ‘from scratch’ and proceed normally.

  • Thomas Eyde

    As jdn suggests, creating new specs is one way to do it. Your design / architecture has changed, so this is a valid option.

    My first reaction, however, was that your existing abstraction is no longer sufficient. My guess is that since this user control will be used in several places, it will need a view model on its own. Here’s what I think I would do:
    1. Create the user control and move all existing UI gadgets over there.
    2. Add the user control to the form
    3. Duplicate the part of the view interface affecting the user control
    4. Let the view pass data and actions between the old interface and the new.
    5. Create a new view model inside of the user control and populate it with data from the interface
    6. Make sure that the user control only use the view model
    7. Change the user control interface to only use the new view model.
    8. Change the original interface to use the new view model.
    9. Remove any leftovers if not already done.

    And of course, run my tests at every critical step, keeping them green all the way. The idea is to start the refactoring behind the outermost abstraction the tests can see. Then it should be easier to keep the tests both compiling and green.

    (PS. Posting doesn’t work in Chrome)

  • I would tend to agree with jdn.

    It has been a bit since I worked with Windows Forms and controls, but you also might consider creating a custom control instead of a user control. I find it us usually a cleaner and testable end result. It is very hard to test user controls.

    I would treat it as new code, creating a failing unit test just like you would with new code. The only difference would be that sometimes, when you implement a method you will be able to copy and past it from the previous structure. (Although you will probably find that you will want to rewrite completely large parts of it.)

  • @gary,

    that’s close to what i do, but i do it backwards. good call for getting it done quickly.


    excellent advice as always. :)

    @jdn / @thomas / @john

    while i agree in principle, i don’t think that i have the luxury of taking the time to do this in my current situation. it may be reasonable to take this approach in other situations, though, and i appreciate the advice on this. it’s a good approach and one that i had thought about, but had not yet tried.

    thanks for the responses, everyone! i’m definitely going to be trying these things out.

  • Joshka

    Did you end up reading Working Effectively with Legacy Code? Might be worth a re-visit.

  • Joshka

    Did you end up reading Working Effectively with Legacy Code? Might be worth a re-visit.

  • @Joshka – it’s been a while, but yes. i should go back and read it again. i wasn’t really dealing with any legacy code the first time i read (mostly skimmed) through it.

  • Thomas Eyde

    @derick, what is so time consuming about my steps? You do have a refactoring tool, right?

  • @Thomas – D’OH! sorry… i meant to respond to your larger comment separately from the “as jdn suggests” bit… you’re longer suggestion is pretty close to what i ended up doing. :)