The Source for Java Technology Collaboration
User: Password:



Tom White

Tom White's Blog

More Literate Programming with jMock: Anaphora

Posted by tomwhite on May 14, 2006 at 02:30 AM | Comments (5)

According to the dictionary, an anaphor is a word used to avoid repetition. It refers back to something in the conversation. The word "it" in the previous sentence refers back to the word "anaphor" in the first sentence, so "it" is an anaphor for "anaphor". Natural language is often ambiguous, and one reason for this is that it may not be clear which word an anaphor such as "it" is referring to.

But ambiguity and programming languages don't go very well together - so why would anyone want to mix the two? Actually, there are circumstances in programming languages where there is no real ambiguity, and an anaphor can have a use in eliminating repetition. Think of it as applying the DRY (Don't Repeat Yourself) principle at the syntax level.

jMock

We introduced a limited form of anaphora in the functional test framework I talked about in my previous blog entry, Literate Programming with jMock. Consider some more sample code:

public void testMainMenu() throws Exception {

    goToTheIndexPage();
    assertThat(theCurrentPage, has(title("Home Menu")));
    assertThat(it, has(menuOptions().named("Departures and Arrivals",
                                           "Travel News")));
}

The variable it refers to the object that was the target of the assertThat call on the previous line; in this case the current page referred to by the theCurrentPage variable. We improve readability and remove multiple references to theCurrentPage, without compromising ambiguity (and therefore without the risk of introducing bugs).

So where does the the variable it come from? We created a subclass of org.jmock.MockObjectTestCase, and added a protected instance variable called it of type Object, then overrode the assertThat method to stash away the target object:

@Override
public void assertThat(Object target, Constraint constraint) {
    it = target;
    super.assertThat(target, constraint);
}

That's it.

Temporary Local Variables

The biggest gain is the case where you can avoid having to create a temporary local variable. You often want to assert several things about an object, so you end up creating a local variable with a short name to refer to the target object:

public void test() {
    Fruit fruit = lunchbox.getFruit();
    assertThat(fruit, isNotNull());
    assertThat(fruit, hasProperty("colour", eq("yellow")));
    assertThat(fruit, isIn(lunchbox.getItems()));
}

Alternatively, you can use the and constraint to make a single assertion, but this is less readable and is hard to format well:

public void test() {
    assertThat(lunchbox.getFruit(),
        and(isNotNull(),
            hasProperty("colour", eq("yellow")),
            isIn(lunchbox.getItems())));
}

Using it produces the most readable test:

public void test() {
    assertThat(lunchbox.getFruit(), isNotNull());
    assertThat(it, hasProperty("colour", eq("yellow")));
    assertThat(it, isIn(lunchbox.getItems()));
}

Our experience has been that the little bit of syntactic sugar that it provides is convenient, and not at all ambiguous.


Bookmark blog post: del.icio.us del.icio.us Digg Digg DZone DZone Furl Furl Reddit Reddit
Comments
Comments are listed in date ascending order (oldest first) | Post Comment

  • This is either really clever, or evil, or both :)

    If you want to go this way, how about:

    public void testFruitInLunchbox() {
    assertThat( lunchboxFruit, isNotNull() );
    andIt( isYellow());
    andIt( isIn( lunchboxItems ));

    Which would make a clearer distinction between the starting and subsequent clauses.

    Posted by: smgfreeman on May 14, 2006 at 10:31 AM


  • Nice suggestion - it also makes it even more readable. So the implementation would be (given the above extension of assertThat):

    public void andIt(Constraint constraint) {
    assertThat(it, constraint);
    }

    There's nothing to stop having both forms available.

    Will this be in the next jMock/Hamcrest, I wonder?


    Tom

    Posted by: tomwhite on May 14, 2006 at 11:03 AM


  • Our first implementation, as Tom showed, allows for fairly arbitrary manipulation of 'it' in the test, which could be viewed as evil.


    With Steve's suggestion, we could make 'it' private in the base testcase class, and only allow it to be set by assertThat() and read by andIt(). For grammatical correctness I think it should possibly by andThatIt(...), but I could be opening a whole can of worms :-)


    My example for when you want to use this sort of thing is when the target (subject?) of your assertion is a complex expression, e.g.:

    assertThat(theMug().on(theDesk()).by(theWindow()), isColoured(red));
    andThatIt(isFilledWith(tea));

    Robert

    Posted by: robertchatley on May 14, 2006 at 03:11 PM

  • Jaggregate ("a collections library that is modeled after the ANSI Smalltalk collection protocols") has a similar class to Constraint - UnaryPredicate, with a "boolean matches(A target)" method.

    However, it's an abstract class instead of an interface, with methods such as "UnaryPredicate<A> and(UnaryPredicate<? super A> other)".

    So you can do "predicate1.and(predicate2)" instead of "and(predicate1, predicate2)".

    So you could have:


    public void test() {
    assertThat(lunchbox.getFruit(),
    isNotNull()
    .and(hasProperty("colour", eq("yellow")))
    .and(isIn(lunchbox.getItems())));
    }

    as an alternative.

    Posted by: calumm on May 17, 2006 at 01:58 PM

  • What about that?

    public void test() {
    assertThat(lunchbox.getFruit()).
    .isNotNull()
    .and(hasProperty("colour", eq("yellow")))
    .and(isIn(lunchbox.getItems())));
    }

    Anyway very good idea.
    Keep it up!
    Check this http://piotrga.wordpress.com/2006/12/06/literate-testing-extending-jmock/

    Posted by: piotrga on December 06, 2006 at 09:05 AM



Only logged in users may post comments. Login Here.


Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds