The Artima Developer Community
Sponsored Link

Weblogs Forum
Vise - A Testing/Refactoring Tool for Java

11 replies on 1 page. Most recent reply: Jul 24, 2008 3:40 AM by Bob Lauer

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 11 replies on 1 page
Michael Feathers

Posts: 448
Nickname: mfeathers
Registered: Jul, 2003

Vise - A Testing/Refactoring Tool for Java (View in Weblogs)
Posted: Aug 8, 2006 8:27 AM
Reply to this message Reply
Summary
A simple tool which makes refactoring safer.
Advertisement

In my last blog, I described sensing variables and how you can use them to write tests for gnarly long methods when you want to refactor. As I mentioned, it’s a useful technique, but it is a pain to set up. A few weeks ago, I wrote a little tool which makes the process much easier. It’s called Vise and it uses the metaphor of a vise, a clamp that you use to hold down mechanical parts when you are working on them.

Here’s how it works...

// Exhibit A – Big Gnarly Method

public class RPRequest {
    ...
    public int process(int level, RPPacket packet) {
        if  (...) {
            if (...) {
                ...
            } else {
                ...
                bar_args[1] += list.size();
                packet.add(new Subpacket(list, arrivalTime));
                if (packet.calcSize() > 2)
                    bar_args[1] += 2; 
            }
        } else {
            int reqLine = -1;
            bar_args[0] = packet.calcSize(reqLine);
            ...
	}
    }
}

We’d like to do some refactoring inside this method, but we know that some of the things that we want to do (changing the order of statements, etc) could affect the contents of the bar_args array if we make a mistake. Sadly, we don’t have access to the bar_args array; it’s used internally in the method and inaccessible to us. What can we do? Well, we can use sensing variables, but there is another alternative. We can use Vise.

Essentially, Vise is a tool that helps you record a set of values that occur in your code and then use those values as a behavioral invariant. Once the values are recorded, if they change the next time the code executes, Vise throws an exception.

Here’s how we can use Vise to clamp down the code above. The first thing we do is go back to our code and add a series of calls to a static method named grip on a class called Vise:

import vise.tool.*;

public class RPRequest {
    ...
    public int process(int level, RPPacket packet) {
        if  (...) {
            if (...) {
                ...
            } else {
                ...
                bar_args[1] += list.size();
                Vise.grip(bar_args[1]);
                packet.add(new Subpacket(list, arrivalTime));
                if (packet.calcSize() > 2)
                    bar_args[1] += 2; 
                Vise.grip(bar_args[1]);
            }
        } else {
            int reqLine = -1;
            bar_args[0] = packet.calcSize(reqLine);
            Vise.grip(bar_args[0]);
            ...
	}
    }
}

Next, we write a series of tests which call the process() method with various inputs:


// Note that the test class inherits from ViseTest..

public class RPRequest extends ViseTest {
    public void testProcessSimpleRequest() {
        final int BASE_LEVEL = 0;
        new RPRequest().process(BASE_LEVEL, new RequestPacket(“local”));
    }

    public void testProcessLoginRequest() {
        final int TARGET_LEVEL = 1;
        new RPRequest().process(TARGET_LEVEL, new LoginPacket(“mike”, “default”));
    }
    ...
}

Then we execute the tests, and they all pass.

They are pretty silly looking tests, aren’t they? They don’t have any assertions. Well, here’s why: The first time that you execute a test method that calls grip(), Vise checks to see if there is a vise file for that method on your system. If there isn’t, it creates a file and then takes each value that you pass to grip and saves it to that file. If the file already exists, Vise will open it and read the next value from it each time grip is called. If the file value matches the gripped value, all is fine. If there is a difference, Vise throws an exception that indicates that the behavior of your code has changed since you recorded those values.

The algorithm for using Vise is simple:

  1. Look at a chunk of code that you need to modify or refactor, and figure out what intermediate values you’d like to preserve.
  2. Add grip calls
  3. Write tests which exercise those paths
  4. Refactor or make your changes
  5. Delete the grip calls from your code

When I’ve used it, I’ve used it to convince myself that I’m preserving behavior as I start to get code under test. It makes the work easier.

Vise has a few more ease of use features. There is a method on Vise called Vise.inspect() which throws an exception that shows you all of the values that have been gripped in the current test method. When the exception is caught by JUnit, it gives you a nice listing of values. This can be very handy when you are trying to figure out whether particular tests are exercising a particular chunk of code. Vise also has a method named Vise.release() which deletes the persisted file for each method. In practice, however, I’ve found it easier just to keep a window open on my desktop which shows all of the vise files for a set of tests. When I want to re-record, I just delete one or more of the files.

Is Vise ideal? No. I think it would be cool to have an IDE plug-in that does this. I can imagine going into my IDE, selecting a set of expressions across my code base, and then associating them with a set of tests. Once I’ve associated them I should be able to use some set of buttons on my IDE to clamp down the current behavior or release it. With that I could always know whether I’m inadvertently changing behavior I care about.

You can get a copy of the Vise code in a zip here. It contains a readme file, but be forewarned: the tool hasn't seen extensive use. Giovanni Asproni and I are piecing together a version for C++. Please let me know if you give the Java version a spin and find it useful (or not).


Michael Sims

Posts: 2
Nickname: hironimus
Registered: Aug, 2006

Re: Vise - A Testing/Refactoring Tool for Java Posted: Aug 8, 2006 11:00 AM
Reply to this message Reply
Sounds like a very cool utility (and I also enjoyed your previous blog entry about "sensing variables"). However it appears that the link to the zip containing the code is invalid. It's pointing to a GIF image, not a zip file...

Michael Feathers

Posts: 448
Nickname: mfeathers
Registered: Jul, 2003

Re: Vise - A Testing/Refactoring Tool for Java Posted: Aug 8, 2006 11:07 AM
Reply to this message Reply
> Sounds like a very cool utility (and I also enjoyed your
> previous blog entry about "sensing variables"). However
> it appears that the link to the zip containing the code is
> invalid. It's pointing to a GIF image, not a zip file...

Hmm... I just downloaded it again, and it seems to be working for me. Can you try again?

Here's the link location copied from my blog above: http://www.objectmentor.com/resources/downloads/bin/Vise1.0.zip

Michael Feathers

Posts: 448
Nickname: mfeathers
Registered: Jul, 2003

Re: Vise - A Testing/Refactoring Tool for Java Posted: Aug 8, 2006 2:03 PM
Reply to this message Reply
Okay. I figured out the problem. The file is now at a new location: http://www.objectmentor.com/resources/downloads/bin/Vise.zip

I changed the link in my blog to point to that url as well.

Michael Sims

Posts: 2
Nickname: hironimus
Registered: Aug, 2006

Re: Vise - A Testing/Refactoring Tool for Java Posted: Aug 8, 2006 3:09 PM
Reply to this message Reply
Cool, it works now. Thanks for sharing this, I believe it will come in handy...

Alex Bacon

Posts: 1
Nickname: alexbacon
Registered: Aug, 2006

Re: Vise - A Testing/Refactoring Tool for Java Posted: Aug 13, 2006 7:38 AM
Reply to this message Reply
After spending almost a year developing using mocks, JUnit and TestNG - it seems to me that most (non-web) unit testing boil down to verifying the outputs for a particular set of inputs and what external calls are made (which is where mocks comes in). Much of writing tests with mocks is drudgery - but is essential to allow proper unit testing of individual methods in individual classes. Combining the Vise idea inside an IDE which could generate JUnit code + test data (ideally stored within the code) and prompt the user to enter in the expected return values for calls outside of the individual component being tested would be fantastic. Even more so if this can be set up so the tests can be debugged and fixed up with the same editor as changes are made to the underlying code - replacing the expected results as required.

Brian Slesinsky

Posts: 43
Nickname: skybrian
Registered: Sep, 2003

Re: Vise - A Testing/Refactoring Tool for Java Posted: Aug 26, 2006 8:03 PM
Reply to this message Reply
Vice.grip() looks a lot like Logger.log(). I wonder if you could do something similar by adding log statements to your code and having a LogTest class that automatically verifies that each test logs the same messages as before? That way these extra calls could also be used for diagnostics when in production.

Michael Feathers

Posts: 448
Nickname: mfeathers
Registered: Jul, 2003

Re: Vise - A Testing/Refactoring Tool for Java Posted: Aug 27, 2006 7:12 AM
Reply to this message Reply
> Vice.grip() looks a lot like Logger.log(). I wonder if
> you could do something similar by adding log statements to
> your code and having a LogTest class that automatically
> verifies that each test logs the same messages as before?
> That way these extra calls could also be used for
> r diagnostics when in production.

Actually, there is something that does that. It is called TextTest and it was written by Geoff and Emily Bache. The link's not coming up right now but here it is: http://texttest.carmen.se/

It's great when you already have logging. I suspect that the choice between using that and something like vise depends on whether you have logging, and how permanent you want your invariants to be.

Ionut Scutaru

Posts: 1
Nickname: scutaru
Registered: Jun, 2008

Re: Vise - A Testing/Refactoring Tool for Java Posted: Jun 14, 2008 11:32 PM
Reply to this message Reply
I was wondering.. those grip calls need to stay in your code after you refactor your code ? I wouldn't want to pollute the code with automated testing stuff..

Michael Feathers

Posts: 448
Nickname: mfeathers
Registered: Jul, 2003

Re: Vise - A Testing/Refactoring Tool for Java Posted: Jun 15, 2008 7:12 AM
Reply to this message Reply
> I was wondering.. those grip calls need to stay in your
> code after you refactor your code ? I wouldn't want to
> pollute the code with automated testing stuff..

No. You take them out. That's the hardest thing to explain about this. People aren't used to putting code in temporarily and ripping it out when they work.

The ideal thing with vise is to have it be an IDE feature so that you can select grip points in the IDE just like you can select breakpoints in a debugger.

Bob Lauer

Posts: 5
Nickname: rfl4711
Registered: Jul, 2008

Re: Vise - A Testing/Refactoring Tool for Java Posted: Jul 23, 2008 7:50 AM
Reply to this message Reply
Michael, it seems that the link is broken again. Could you update it please?

Cheers,

Robert.

Bob Lauer

Posts: 5
Nickname: rfl4711
Registered: Jul, 2008

Re: Vise - A Testing/Refactoring Tool for Java Posted: Jul 24, 2008 3:40 AM
Reply to this message Reply
... found a working link on the objectmentor page and tried
it on a current project. Results:

- There is a lot of Vise errors that seem to arise because
Vise is in an inconsistent state.
- When setting up the tests I have to refactor my legacy code
so much that I end up with a lot of hooks where regular
unit testing can be applied as well (i.e. assertEqual(expected, actual) instead of Vise(currentState)) - which is even better, since it serves as documentation on
what exactly the state of interest is.
- What vises would be really useful for is "history" regression testing, a.k.a. diff debugging: I have documented
undesired behavior in the current codebase by means of a test or a "vise config" or a mixture of techniques and want to know
if there was a point in the past when this behavior did not show up. With "local" version management systems (git, Bazaar et al) this would be quite fast (the bottleneck is probably your build). Then the test system would pinpoint the class(es) that changed between working and non-working
versions.

Flat View: This topic has 11 replies on 1 page
Topic: Layers, Levels, and DIP Previous Topic   Next Topic Topic: Simplyfing Java Generics by Eliminating Wildcards

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use