Programming

my faceChris Foley is a computer programming enthusiast. He loves exploring new programming languages and crafting nice solutions. He lives in Glasgow, Scotland and works as a software developer.

Testing a concept with JUnit

Welcome to my first blog post.

JUnit is a framework for running automated tests on your code. Essentially, you write tests to make sure different parts of your code are working properly. It really comes into its own when you make changes to your project. If something breaks, your tests should pick it up. Not only do you know that a bug has been introduced, you know the class and usually the method. It's much more efficient than manual testing as all the tests you've ever written for your project can be run at the click of a button. If you've not heard of unit testing or JUnit, take a look here: junit.org.

I want to explore an aspect of organising unit tests using an example from my current project. I'm writing an application to draw battle report pictures for tabletop war games. (You know the kind – painted models, rulers, dice).

I've got an interface Rendarable which defines methods to do with displaying all the shapes in the diagram. Implementations include: Model, KillMarker, MoveEventMarker, ShootingMarker and Unit (unit as in a squad of soldiers). The interface Renderable looks a bit like this:

public interface Renderable {
	
	public void getFootprint( GameEvent event, Area output );
	public Paint getFillPaint();
	public Paint getOutlinePaint();
	public Stroke getStroke();
	public boolean isDisplayedAtEvent(GameEvent currentEvent);
	public int getZOrder();
	
} // Renderable

When writing unit tests, most people have a test case for each class in their project. In this example I would have a ModelTest class that looks something like:

import static org.junit.Assert.*;

import org.junit.Before;
import org.junit.Test;

public class ModelTest2 {

	@Before
	public void setUp() throws Exception {
	}

	@Test
	public void testGetFootprint() {
		// Insert test here
	}

	@Test
	public void testGetFillPaint() {
		// Insert test here
	}

	@Test
	public void testGetOutlinePaint() {
		// Insert test here
	}

	@Test
	public void testGetStroke() {
		// Insert test here
	}

	@Test
	public void testIsDisplayedAtEvent() {
		// Insert test here
	}

	@Test
	public void testGetZOrder() {
		// Insert test here
	}

}

According to this convention I would have a similar KillMarkerTest class, etc. In fact, there's nothing wrong with this way of organising tests and that's exactly what I do have. But it's not the only way to organise tests and the getZOrder() method needs something different.

In case you're not into 2D graphics, when 2 sprites overlap the higher z-order sprite will be displayed on top of the lower z-order sprite (or at least that's the convention I'm using). It's not important that the class Model has a z-order of 5 (or whatever) which is all that I could reasonably check in the above test class. What is important is that models are displayed on top of arrows but below kill markers.

To test this concept, I have a test class that compares the z-orders of each type of Renderable, ensuring all the relationships are correct. Here it is:

import java.awt.*;
import static org.junit.Assert.*;
import org.junit.*;

public class ZOrderTest {
	
	private KillMarker killMarker;
	private Model model;
	private MoveEventMarker moveMarker;
	private ShootingEventMarker shootMarker;
	private Unit unit;

	@Before
	public void setUp() throws Exception {
		GameEvent event = new GameEvent();
		Area shape = new Area(new Rectangle(0,0,1,1));
		Color col = Color.black;
		Stroke stroke = new BasicStroke();
		model = new Model(event, shape, col, col, stroke);
		killMarker = new KillMarker(event, col, col, stroke, model);
		moveMarker = new MoveEventMarker(model, event, col, col, stroke);
		shootMarker = new ShootingEventMarker(model, model, event, col, col, stroke);
		unit = new Unit(null, event, col, col, stroke);
	}
	
	@Test
	public void testZOrder_kill_model() {
		assertTrue( killMarker.getZOrder() > model.getZOrder() );
	}

	@Test
	public void testZOrder_kill_move() {
		assertTrue( killMarker.getZOrder() > moveMarker.getZOrder() );
	}

	@Test
	public void testZOrder_kill_shoot() {
		assertTrue( killMarker.getZOrder() > shootMarker.getZOrder() );
	}
	
	@Test
	public void testZOrder_model_move() {
		assertTrue( model.getZOrder() > moveMarker.getZOrder() );
	}

	@Test
	public void testZOrder_model_shoot() {
		assertTrue( model.getZOrder() > shootMarker.getZOrder() );
	}
	
	@Test
	public void testZOrder_move_shoot() {
		assertEquals( moveMarker.getZOrder(), shootMarker.getZOrder() );
	}
	
	@Test
	public void testZOrder_unit_kill() {
		assertTrue( unit.getZOrder() < killMarker.getZOrder() );
	}

	@Test
	public void testZOrder_unit_model() {
		assertTrue( unit.getZOrder() < model.getZOrder() );
	}

	@Test
	public void testZOrder_unit_move() {
		assertTrue( unit.getZOrder() > moveMarker.getZOrder() );
	}

	@Test
	public void testZOrder_unit_shoot() {
		assertTrue( unit.getZOrder() > shootMarker.getZOrder() );
	}
}

For a while I got stuck in a rut, trying to fit all my tests for a class in the one test case. I suppose I could have done that with this example too, but really ModelTest shouldn't even know KillMarker exists, for example. This way, all the z-order tests are kept in one simple class and aren't scattered throughout my test suite.

Recently, I've begun to look for concepts to test more and more. Not only does it keep my test code more organised, it encourages me to test the things that matter. Using the "one test case for each class" convention, I could have checked that Models had a z-order of 5 and KillMarkers had a z-order of 3. My tests would have passed and I'd have thought everything was hunky dory, not realising those numbers would have actually displayed the sprites in the wrong order.

Thanks for reading and I hope you found this interesting or useful.

25 November 2008

Comments