TechHui

Hawaiʻi's Technology Community

I like the Hamcrest matcher API, assertThat(), in JUnit 4.4. It has advantages for non-equal assertions:
* useful error messages in the test report, and
* enabling refactoring of duplicate code.

error messages

int foo = 100;
assertThat(foo, lessThan(42));

java.lang.AssertionError:
Expected: a value less than <42>
got: <100>

This is good for intermittent failures, when you don't get the bad value when you rerun the test. Without Hamcrest, assertTrue(foo < 42) doesn't report what you got (or expected). Another example:

List<String> foo = Arrays.asList("fi", "fai", "fo");
assertThat(foo, hasItem(endsWith("um")));

java.lang.AssertionError:
Expected: a collection containing a string ending with "um"
got: <[fi, fai, fo]>

duplicate code

Hamcrest turns the test condition into an object, so it can be passed as a parameter (like a lambda). That allows the refactoring of duplicate code. For example, if we had three test methods like this:

@Test
public void fetchByGroup_zero() {
List<Employee> employees = _dao.fetchByGroup(EMPTY_GROUPID.value);
assertThat(employees.size(), equalTo(0));
}

@Test
public void fetchByGroup_one() {
List<Employee> employees = _dao.fetchByGroup(ONE_GROUPID.value);
assertThat(employees.size(), equalTo(1));
}

@Test
public void fetchByGroup_multi() {
List<Employee> employees = _dao.fetchByGroup(MULTI_GROUPID.value);
assertThat(employees.size(), greaterThan(1));
}

Because the test condition is a Matcher object, the duplicate code can be factored out like this:

private void assertThatGroupSize(GroupId id, Matcher<Integer> expected) {
    List<Employee> employees = _dao.fetchByGroup(id.value);
assertThat(employees.size(), expected);
}

@Test
public void fetchByGroup_zero() {
assertThatGroupSize(EMPTY_GROUPID, equalTo(0));
}

@Test
public void fetchByGroup_one() {
assertThatGroupSize(ONE_GROUPID, equalTo(1));
}

@Test
public void fetchByGroup_multi() {
assertThatGroupSize(MULTI_GROUPID, greaterThan(1));
}

Views: 855

Replies to This Discussion

By the way, I can't format code nicely on this discussion board.
Very interesting. It looks LISPy :-) On the Hamcrest project page it also says the framework can be used for UI validation rules. I'll have to check that out.
It would be great to have Confluence's code macro with multiple language support. I think this will get better when they introduce the wiki functionality in a few months. If not, we can probably hack it in.

I setup a forum for people to suggest features.
I don't know LISP well enough to compare, but one difference may be that Hamcrest is statically typed. (It also needs static imports, which have been a little inconvenient as I've been learning it.) I've never used the builder pattern or a fluent API to this extent before.

Martin Fowler has a good discussion of this pattern on the second half of this page from a book prototype. (The half starts with "Example: JMock - Builder Object with Multiple Types (Java)".)
http://martinfowler.com/dslwip/ExpressionBuilder.html
The part toward the end was especially interesting. For method chaining, he explains returning a particular interface of the builder object to impose a grammar (letting the IDE help with auto-complete). The next method in the chain may return a different interface of the same builder object.
My example doesn't have any method chaining, but JUnit does support it. E.g.,

int foo = 88;
assertThat( foo, both( greaterThan(42) ).and( lessThan( 99 ) ) );
> It also needs static imports, which have been a little inconvenient
> as I've been learning it

I find static imports very useful. I frequently statically import the methods from java.lang.Math.

> I've never used the builder pattern or a fluent API to this extent
> before.

This is one of my favorite patterns. Groovy supports this pattern at the library and language level.
I tried out a Groovy builder a couple months ago on my quest for a Java object graph declaration language with strong typing. I want something that looks minimal, like SDL or JSON, but with IDE auto-complete and warnings.

I found such a Groovy builder that somebody had made themselves. I was disappointed that Groovy didn't have one built in already. However, searching for that one to show you here, I just found one that is built in after all.

http://groovy.codehaus.org/ObjectGraphBuilder

I'll have to try it out. Also, I had a problem with IDEA's Groovy support for the one I found before; it didn't run the unit tests properly. That IDEA plug-in is still beta, though.

Ultimately, this way, I doubt I'll get the auto-complete and warnings that I'm looking for, anyway. I wound up making my own builder in Java. It wasn't terrible, although I'd like to have a language take care of all that for me.
Todd pointed out at the coders meeting last night that these test conditions are called predicates.

I also realized that the Interpreter pattern is more relevant than the Builder. The predicates are an abstract syntax tree of a domain specific language.

A scripting language could do this more elegantly by outputting the predicate itself to the test report as an embedded DSL, instead of needing to render the predicate. But Hamcrest is nice if you're using Java, and it has the advantages of strong typing.
Hey Dave - I finally figured out how to post code with proper formatting. I used it here. If you put your code in <pre> tags and replace all the new lines with <br> tags it eliminates the problem with excess new lines and weird whitespace.

RSS

Sponsors

web design, web development, localization

© 2020   Created by Daniel Leuck.   Powered by

Badges  |  Report an Issue  |  Terms of Service