I came up with some neat code for data preparation today. For unit testing you often have to prepare data. Unit tests should act as documentation for how the code works and thus it should be as easy to quickly read and digest as possible.

I’ll walk though several attempts at coming up with unit testing data.

First run


Alert a1 = new Alert();
a1.setText("test text");
a1.setTimestamp(new Date());
a1.setToken("token1");
interestedInThisToken.add("token1");

Alert a2 = new Alert();
a2.setText("test text");
a2.setTimestamp(new Date());
a2.setToken("token2");

Alert a3 = new Alert();
a3.setText("test text");
a3.setTimestamp(new Date());
a3.setToken("token1");
interestedInThisToken.add("token3");

Second Run

Run one was reasonably readable, but when you are building a lot of data or setting more fields there ends up being a fair bit of gruff you need to wade through to see the pertinent details. Lets clean that up a bit by introducing a helper function.


addAlert("test text", new Date(), "token1");
interestedInThisToken.add("token1");
addAlert("test text", new Date(), "token2");
addAlert("test text", new Date(), "token3");
interestedInThisToken.add("token3");

That’s much better. But we can go one better to get rid of that extra line. One problem with that line is you are repeating text and may get it wrong and thus make bad data. Obviously this is something you want to avoid when creating test data that is meant to prove your code is correct. So you could do this:


String tokenText = "token1";
addAlert("test text", new Date(), tokenText);
interestedInThisToken.add(tokenText);

But now an extra line has been added. Surely we can inline this by passing a flag.


addAlert("test text", new Date(), "token1", ADD_TOKEN_TO_INTERESTED_LIST);

We just overload addAlert() to take a boolean.

That’s not too bad. But let’s complicate the matter. Say we want to output the list of tokens we collect sometimes, and other times we just want the text. Easy, add another argument and overload addAlert() again:


addAlert("test text", new Date(), "token1", ADD_TOKEN_TO_INTERESTED_LIST, OUTPUT_AS_LIST);

But now we need 4 methods:


addAlert(....)
addAlert(boolean)
addAlert(int)
addAlert(boolean, int)

If we add another argument then we need another 4 methods. Its not a very scalable idea.

Third Run

There are a number of solutions and I’d like to write about the one I’ve developed.


public abstract class BaseArg {
	private static BaseArg instance = null;
	private List baseArgs = new ArrayList<>();
	private String lastBaseArg;

	private BaseArg() {
	}

	private static void make() {
		if (instance == null) {
			instance = new BaseArg();
		}
	}

	/**
	 * Clear the list of args (the list has been around since the JVM started).
	 */
	public static void clear() {
		if (instance != null) {
			instance.baseArgs.clear();
			instance.lastBaseArg = null;
		}
	}

	/**
	 * Add subsequent arg after the first one is added through {@link #create(String)}
	 * 
	 * @param arg
	 */
	public void add(String arg) {
		this.lastBaseArg = arg;
		this.baseArgs.add(arg);
	}

	/**
	 * 
	 * @return the arg just added
	 */
	public String out() {
		return lastBaseArg;
	}

	/**
	 * 
	 * @return the list of all args added (ie. since the JVM started). Call {@link #clear()} to clear the list of
	 *         args.
	 */
	public List outAsList() {
		return baseArgs;
	}

	/**
	 * This is the first method to call.
	 * 
	 * @param arg
	 *            we are saving.
	 * @return the new instance we can call other methods on.
	 */
	public static BaseArg create(String arg) {
		make();
		instance.add(arg);
		return instance;
	}

	/**
	 * We don't want the added arg to be added to the List since we only want to add ones that are of interest to us.
	 */
	public BaseArg noList() {
		baseArgs.remove(baseArgs.size() - 1);
		return instance;
	}
}

Some of you may recognise the Singleton pattern.

Just create the class for the type you want to pass in as arguments. This one is just for Strings, however you could easily create a Generics version for any type:


public class Token extends BaseArg {
}

Now you can write your data building code more succinctly and with infinite possible ways to modify how the data is being worked:


# token1 is added to the list
addAlert("test text", new Date(), Token.create("token1").outAsList());

outAsList() says to output the full list


# token2 is NOT added to the list
addAlert("test text", new Date(), Token.create("token2").noList().out());

noList() says not to add the argument to the list
out() says to output just the text ie. ‘token2’

I can spot 2 potential problems here but they are easily fixed, and this is code only developers are going to work with anyway:

  1. What if you want to have 2 lists – 1 for things interested in, the other for the list of tokens. ANS: No problems just update the baseArg class or even a subclass if that is better
  2. What if you don’t make the fix as per point 1 and and then still say .noList().outAsList(); Although not a bug it seems silly that you’d pass a new token in but not do anything with it. If you do want to prevent this problem then you could update outAsList() to only output the list if the last item is the same as lastBaseArg.

Happy testing!

Written on August 31st, 2013 , Java, Unit Testing Tags:

Brooke Smith is proudly powered by WordPress and the Theme Adventure by Eric Schwarz
Entries (RSS) and Comments (RSS).

Brooke Smith

Portfolio and site of a software engineer