One of the things I miss in going from .NET back to the Java world is .NET properties. Yes, it was just a code hack that was really rewritten as setters in the CLR, but it was a /nice/ hack. ;)
In other words, I could get and set what appeared to everything to be a field, but was really a method. So I could say myObject.Name = "My Name"
with the following code:
public String Name
{
get{return name;}
set{name = value;}
}
where value
is a special keyword for the value passed in for setters.
I find myself mostly wanting this in the Java version of Fitnesse where to have it set values you have to expose a public field. Since technically your class Fit is calling should just immediately delegate off to the real business objects, it’s not usually a big deal. However, since the Python and .NET versions allow it, and the Java one doesn’t, I wanted to get it working.
It turns out that it is a fairly painless change, but requires modification to three core files in the fit
package of Fitnesse. So, first, you need to download the source, and make sure you also have Ant so that you can build the package.
Got it? Good. The first file to change is fit.Fixture (Fixture.java in the fit directory). At line 310, add the following (where commented – surrounding text is the context):
else if (type.equals(Date.class))
{
return DateFormat.getDateInstance(DateFormat.SHORT).parse(s);
}
//Add to handle void objects
else if (type.equals(void.class))
{
return s;
}
//End Add
else if (hasParseMethod(type))
{
return callParseMethod(type, s);
}
Next, modify fit.TypeAdapter (TypeAdapter.java) to handle VoidAdapters. The first change is at line 58, to add a void handler:
if(type.equals(boolean.class)) return new BooleanAdapter();
//Add for VoidAdapter
if(type.equals(void.class)) return new VoidAdapter();
//end Add
throw new UnsupportedOperationException("can't yet adapt " + type);
Second, at line 137 to actually define a VoidAdapter:
// Subclasses ///////////////////////////////
//Add VoidAdapter
static class VoidAdapter extends TypeAdapter
{
public void set(Object i) throws IllegalAccessException, InvocationTargetException
{
Object[] params = new Object[]{i};
method.invoke(target, params);
}
}
//End Add
static class ByteAdapter extends ClassByteAdapter
{
public void set(Object i) throws IllegalAccessException
{
field.setByte(target, ((Byte) i).byteValue());
}
}
Finally, we change fit.Binding (Binding.java) so that when a column matches the normal field regex, it tries to instatiate a method as well as a field. Change the method makeAdapter
to the following:
private static TypeAdapter makeAdapter(Fixture fixture, String name) throws Throwable
{
Matcher matcher = methodPattern.matcher(name);
if(matcher.find())
return makeAdapterForMethod(name, fixture, matcher);
else
{
TypeAdapter adapter = null;
matcher = fieldPattern.matcher(name);
if(matcher.find())
{
try
{
adapter = makeAdapterForMethod(name, fixture, matcher);
}
catch(NoSuchMethodFitFailureException ex)
{
adapter = makeAdapterForField(name, fixture);
}
}
else
{
adapter = makeAdapterForField(name, fixture);
}
return adapter;
}
}
and change makeAdapterForMethod to the following:
private static TypeAdapter makeAdapterForMethod(String name, Fixture fixture, Matcher matcher)
{
Method method = null;
if(GracefulNamer.isGracefulName(name))
{
String simpleName = GracefulNamer.disgrace(name).toLowerCase();
method = findMethod(fixture, simpleName);
}
else
{
String methodName = matcher.group(1);
method = findMethod(fixture, methodName.toLowerCase());
}
if(method == null)
throw new NoSuchMethodFitFailureException(name);
return TypeAdapter.on(fixture, method);
}
With these changes all Unit and Fitnesse tests should pass – save one: testUseOfGracefulNamingForMethods()
in fit/BindingTest.java
. To make it pass, I changed all of the checkForMethodBinding calls to true:
public void testUseOfGracefulNamingForMethods() throws Throwable
{
checkForMethodBinding("intMethod()", true);
checkForMethodBinding("int Method?", true);
checkForMethodBinding("int method?", true);
checkForMethodBinding("intmethod?", true);
checkForMethodBinding("Intmethod?", true);
checkForMethodBinding("IntMethod?", true);
}
Once you’ve made the changes, run build.xml
and move the fitnesse.jar
file that is created into your Fitnesse installation directory (you probably should rename the existing fitnesse.jar
in case anything goes wrong) and start up your Fitnesse server. I used a test like:
|com.cornetdesign.fitnesse.examples.MethodSetTest|
|methodSet|valueSet?|
|test|test|
|booyah|booyah|
with code:
package com.cornetdesign.fitnesse.examples;
import fit.ColumnFixture;
public class MethodSetTest extends ColumnFixture {
private String val = "";
public void methodSet(String value)
{
val = value;
}
public String valueSet()
{
return val;
}
}
and voila! You should have a green test.
I’ve already sent this off to the Fitnesse guys, but haven’t had any reply from the Java maintainers as to whether this would be usable – so if it is something that is worthwhile to you let me or them know.
Thank you.
I was looking for that !!!
That is sooo needed in FiTNesse – I just wrote a load of code assuming that setters would work (annoyingly they delegated to another object so I’m going to have do a load of rewriting now, grrr)