Saturday, March 28, 2009

Make things testable

So I was going along with my stories and I realized that there's a bunch of stuff that I am not testing. In general with an MVP pattern, you make the V as slim as possible because it's really hard to test things in the view. Can do you write a test that says that your "ok" button actually shows up in the lower right corner of the popup dialog? So you make it really slim so that you just kinda assume that Swing or SWT knows how to render the stuff correctly to the screen and that you are using the API correctly. So with that in mind, I went about writing my game... which is mostly stuff in the view. At this point in the game, there's very little non-view stuff going on. So I'm not testing a whole bunch and that really bothers me.

So I started re-evaluating whether or not my view could be tested. I'm writing this using Java3D which is all java so it should be fairly testable. In the end, to make something show up, you need to add it to your branch graph. So I started looking at ways to inject stubs for my branch groups into my objects and see what gets populated. Unfortunately, Java3D does not code to interfaces. So I made a bunch of adapters around class that implemented interfaces containing the methods I was using. Next I created a bunch of factories to make sure that I always got the same sort of adapter wrapping object back. Then I exposed an interface on the factory and injected that into a few generators that would add my game grid and game pieces. So once I started pulling this out, I realized that very little of my code is actually Java3D code and it it's all really testable.

So I guess the lesson that I've learned with this and a few other projects I've worked on is to try to get around the API/Framework/Whatever that you're working with by using interfaces (and adapters where needed) so you can test your own code.

Here's an example from what I did with the BranchGroup...


public interface IBranchGroup {
void compile();

void addChild(Node node);

void addChild(IBranchGroup child);

BranchGroup getInternal();
}


public class BranchGroupAdapter implements IBranchGroup {
private final BranchGroup branchGroup;

public BranchGroupAdapter(BranchGroup branchGroup) {
this.branchGroup = branchGroup;
}

public void compile() {
branchGroup.compile();
}

public void addChild(Node child) {
branchGroup.addChild(child);
}

public void addChild(IBranchGroup child) {
branchGroup.addChild(child.getInternal());
}

public BranchGroup getInternal() {
return branchGroup;
}
}

public interface IBranchGroupFactory {
IBranchGroup createBranchGroup();
}


public class BranchGroupFactory implements IBranchGroupFactory {
public BranchGroupAdapter createBranchGroup() {
return new BranchGroupAdapter(new BranchGroup());
}
}

public class GameLauncher implements IGameLauncher {
public void launchGame(IUniverse universe, IBranchGroupFactory branchGroupFactory,
IGameEngineFactory gameEngineFactory) {
IBranchGroup branchGroup = branchGroupFactory.createBranchGroup();
IGameEngine gameEngine = gameEngineFactory.createGameEngine(branchGroup);

ISelecterFactory selecterFactory = new SelecterFactory(universe);

gameEngine.createScene(selecterFactory);
gameEngine.createCamera(universe);

branchGroup.compile();

universe.addBranchGraph(branchGroup);
}
}

No comments:

Post a Comment