Thursday, January 14, 2010

Day 8

I am pleased to say I have two new design patterns to blog about today, but these are of a slightly different nature. But first, a quick review of the day.

I spent about half the day today in an iteration meeting, observing and learning about one of 8th Light's productions. Trying to figure out what is specifically going on in a project, with minimal detail, but just a broad outline could develop into a very valuable skill. It is something I frequently see Uncle Bob do. It seems like he doesn't have to read the code to know what is going on, he just sees the shape of the code and immediately can tell the codes intention, and then even a better shape or form it can take to accomplish the same end.

The rest of the day I spent trying to refactor my tests for the KeyProcessor. It was especially challenging because I had never tested a framework like the Dispatcher I used, and so was struggling with the organization.

But now, the two new design patterns.

Design Patterns:

One of the reasons these two patterns are rather different is because they are actually for tests rather than production code. Another reason they are a little different is because I just made both of them! I came across them both in rapid succession, so the second pattern is actually an extension of the first pattern, but can also be used independently. Using a bit of insight from both Micah and Uncle Bob (as well as a nice feature in JUnit) I was able to find a testing strategy that perfectly fit my problem.

The Test Nest Pattern:
-Intent: To create a test file containing several tightly related test classes.
-Motivation: Consider designing tests to describe a case where there is some base abstract class and several small derivative classes that implement one or just a few methods of the base class. This could occur from using the Command Pattern, a Dispatcher/Jump Table, or other similar designs.
Having a file for each of the derived classes would not only seem overkill, since they are small and similar, but it would also make it more difficult to unveil the intent of the code using the tests. Instead, it makes more sense to have all the tests for these derivatives in one file, maybe named after the base class. The issue is that organizing the tests to be in accordance with their specific derivative becomes difficult.
One solution could be making a prefix title for each test, to first indicate which class it is for, and then describe the purpose of the test after the prefix. However this solution would lead to both long and complicated names as well as a test file that is difficult to parse through.
Another solution is the Test Nest Pattern.

-Structure: Using JUnit's SuiteClass technology it is possible to create a Suite Class, and several internal Test Classes.

Say you are trying to process all of the KeyEvents to use for some application. To do this, you create an abstract KeyProcessor class. This class is used as a dispatcher, and in conjunction with an indexing algorithm, it properly selects a derivative class to handle the KeyEvent. Each derivative implements a processKey() function, which is the derivatives only method. Say there is a CmdKeyProcessor and a ShiftKeyProcessor derivative that you are looking to test in the same file.
So you name this file the KeyProcessorSuite and you create two static internal classes named CmdKeyProcessorTest and ShiftKeyProcessor test. Next, you pull out all of the common fields and statically define them at the suite level. You could also statically define a standardSetUp() method that shares common setup steps that both test classes use. You then must add two lines above the suite telling JUnit that this a suite file, and which classes belong to the suite. It looks something like this:

@RunWith(Suite.class)
@Suite.SuiteClasses({KeyProcessorSuite.CmdKeyProcessorTest.class, KeyProcessorSuite.ShiftKeyProcessorTest.class})

public class KeyProcessorSuite{

public static KeyProcessor processor;
public static void standardSetUp(){...}

public static class CmdKeyProcessorTest{
@Before
public void setUp() {
standardSetUp();
processor = new CmdKeyProcessor();
}
@Test
public void canProcessCharacters(){
...
}
}
public static class ShiftKeyProcessorTest{
@Before
public void setUp() {
standardSetUp();
processor = new ShiftKeyProcessor();
}
@Test
public void canProcessCharacters(){
...
}
}
}

And voila! Each derivative class of KeyProcessor can get its own Nest for all its Tests in the KeyProcessorSuite. It provides a simple and intuitive organization for all the tests, and fits it all into a single file.



The Asserter Pattern:
-Intent: To provide a compact set of descriptive and reusable assertions.
-Motivation: There is a general rule that each test should have only one assertion. This could be a single assertEquals(int, int) or it could be a batch of closely related assertions checking the result of a single action. As test after test is written using a batch of similar assertions, even with different check values, there is still a lot of duplicated code. This should be avoided if possible.

One solution to this would be to extract methods that contain batches of reoccurring assertions and that take the check values for arguments. This is a practical solution if all the tests are in a single class, or if there is no underlining theme amongst the extracted assertion methods; however, if you are using a Test Nest Pattern and have several test classes, or if there is a theme in the extracted assertion methods, then this solution wont fit.

This is where you would use the Asserter Pattern.

-Structure: Using a separate or internal class, it is possible to create a class meant specifically to contain all of the assertion methods.

Say you have a TextBoxModel class, which contains all of the information of some abstraction. Say there are a set of actions you preform on the this data, after which you must check a similar set of fields. For example, you might have a set of three fields - int cursorIndex, int selectionIndex, boolean selectionOn - that define a selection, or a range of selected text. Then whenever an arrow key is pressed (perhaps when shift is being held down) you will want to check the values of these three fields. There is also another set of two fields - int cursorIndex, String text - that define the current text state. Whenever a character key or backspace is pressed, you want to check these two values.
You would then create a class (internal or external and static if it is inside a Test Nest Pattern), perhaps named TextModelAsserter, which would contain two assertion methods named - assertSelection, and assertTextState - that contain their respective assertions. It might look something like this:

public static class TextModelAsserter
{
TextModel boxInfo;

public TextModelAsserter(TextModel boxInfo)
{
this.boxInfo = boxInfo;
}

public void assertSelection(int cursorIndex, int selectionIndex, boolean selectionOn)
{
assertEquals(boxInfo.cursorIndex, cursorIndex);
assertEquals(boxInfo.selectionIndex, selectionIndex);
assertEquals(boxInfo.selectionOn, selectionOn);
}

public void assertTextState(int cursorIndex, String text)
{
assertEquals(cursorIndex, boxInfo.cursorIndex);
assertEquals(text, boxInfo.text.toString());
}
}

Next, you create a field of this class - TextModelAsserter asserter - in your test class, and then an instance of it, passing it the appropriate parameters, in a setup method. Using this instance, you can easily make precise and compact assertions, which not only speed up how quickly you can write solid tests, but also gives them a clean and regular look. You could use it something like this:

@Test
public void canProcessBackSpace()
{
processor.processKey(KeyEvent.VK_BACK_SPACE);

asserter.assertTextState(0, "ere are four words");
}

@Test
public void canProcessRightArrow()
{
processor.processKey(KeyEvent.VK_RIGHT);

asserter.assertSelection(2, 4, false);
}
And now you have a quick and easy way to make single lined, well organized, themed assertions throughout a single test class or across several test classes or Nests.



Tomorrow I get to finish writing up my KeyProcessor tests and code using there two new patterns I discovered!

2 comments:

  1. For TestNest, see http://kentbeck.github.com/junit/javadoc/4.8/org/junit/experimental/runners/Enclosed.html

    ReplyDelete
  2. Ah yes, that is in essence the same thing. So much for complete originality!

    ReplyDelete