How to create nested matcher

609 views
Skip to first unread message

Tedd

unread,
Nov 5, 2010, 2:38:18 PM11/5/10
to Hamcrest Java Users
I have a matcher that checks a list over a range for a certain value
[code]
package hamcrest;

import java.util.List;

import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.junit.matchers.TypeSafeMatcher;


public class HasValueInRange extends TypeSafeMatcher<List<? extends
Object>> {
private final Object value;
private final int from;
private final int to;

private HasValueInRange(Object value, int from, int to) {
this.value = value;
this.from = from;
this.to = to;
}

public void describeTo(Description description) {
// TODO Auto-generated method stub
}

@Override
public boolean matchesSafely(List<? extends Object> values) {
// Verify range is valid and in array
if (from > to || to > values.size()) {
return false;
}

for (int i = from; i < to; i++) {
// If I am not equal I am done return false
if (!values.get(i).equals(value)) {
return false;
}
}

return true;
}

@Factory
public static HasValueInRange hasValueInRange(Object value, int from,
int to) {
return new HasValueInRange(value, from, to);
}

}
[/code]

I am using it as follows:
[code]
List<String> test= Arrays.asList(new String[]
{"Hello","Hello","Hello","Goodbye"});
assertThat(test, hasValueInRange("Hello", 0, 2); // This will pass
assertThat(test, hasValueInRange("Hello", 0, 3); // fail last element
is "Goodbye"
[/code]

This works great, however I would really like to be more expressive
and write the assert using nested Matchers, i.e.
[code]
List<String> test= Arrays.asList(new String[]
{"Hello","Hello","Hello","Goodbye"});
assertThat(test, hasValue("Hello", inRange(0, 2)); // This will pass
assertThat(test, hasValue("Hello", inRange(0, 3)); // fail last
element is "Goodbye"
[/code]

So I want to pass an inRange matcher to the hasValue matcher. I am
really struggling on how do implement this. I am just playing with
hamcrest and trying to learn, thanks for any help!

Nat Pryce

unread,
Nov 6, 2010, 9:55:00 AM11/6/10
to hamcre...@googlegroups.com
Hi.  It'll probably be easier the other way.  E.g. hasRange(5. 10. hasItem(equalTo("foo"));


The hasRange function would have the following signature:
    Matcher<List<T>> hasRange(int begin, int end, Matcher<? super List<T>> sublistMatcher)

The sublist matcher matches the sublist of the matched list between begin and end.  You can then use existing Hamcrest matchers to match a sublist that contains an element that matches some other criteria.  In the example above, it matches a sublist that contains an item equal to "foo".

Does that make sense?

    --Nat.



--
You received this message because you are subscribed to the Google Groups "Hamcrest Java Users" group.
To post to this group, send email to hamcre...@googlegroups.com.
To unsubscribe from this group, send email to hamcrest-jav...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/hamcrest-java?hl=en.




--
http://www.natpryce.com

Billy Newman

unread,
Nov 6, 2010, 5:14:28 PM11/6/10
to hamcre...@googlegroups.com
A little, but I am still very confused.

I am having a really hard time writing this matcher.

import java.util.List;

import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.junit.internal.matchers.TypeSafeMatcher;

public class HasRangeMatcher<T> extends TypeSafeMatcher<Matcher<List<T>>>{
private final int from;
private final int to;
private final Matcher<? super List<T>> matcher;
public HasRangeMatcher(int from, int to, Matcher<? super List<T>> matcher) {
this.from = from;
this.to = to;
this.matcher = matcher;
}
@Override
public void describeTo(Description arg0) {
}
@Override
public boolean matchesSafely(Matcher<List<T>> arg0) {
// TODO Auto-generated method stub
return false;
}
@Factory
public static <T> HasRangeMatcher<T> hasRange(int from, int to, Matcher<? super List<T>> matcher) {
return new HasRangeMatcher<T>(from, to, matcher);
}
}

I am not sure if this is what you meant.....but I have no idea how to write the matchesSafely method.  I.E. how do I iterate over a collection of elements and determine if each on matches?

Nat Pryce

unread,
Nov 6, 2010, 5:17:32 PM11/6/10
to hamcre...@googlegroups.com
You don't need to iterate over the elements. The Matchers.hasItem matcher already does that for you, so you can pass that to the constructor.  Then the matchesSafely method is just:

public boolean matchesSafely(Matcher<List<T>> arg0) {
return matcher.matches(arg0.subList(from,to));;
}

(And then some renaming to make it easier to understand).

Cheers,
     --Nat

Billy Newman

unread,
Nov 6, 2010, 5:31:29 PM11/6/10
to hamcre...@googlegroups.com
Ok I made a few mistakes that I fixed but I still cannot get this to compile:

import static org.junit.Assert.assertThat;
import static org.junit.matchers.JUnitMatchers.hasItem;

import java.util.Arrays;
import java.util.List;

import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.junit.internal.matchers.TypeSafeMatcher;

public class HasRangeMatcher<T> extends TypeSafeMatcher<List<T>>{
private final int from;
private final int to;
private final Matcher<? super List<T>> matcher;
public HasRangeMatcher(int from, int to, Matcher<? super List<T>> matcher) {
this.from = from;
this.to = to;
this.matcher = matcher;
}

@Override
public void describeTo(Description arg0) {
}
@Override
public boolean matchesSafely(List<T> list) {
// TODO Auto-generated method stub
return matcher.matches(list.subList(from, to));
}
@Factory
public static <T> HasRangeMatcher<List<T>> hasRange(int from, int to, Matcher<? super List<T>> matcher) {
return new HasRangeMatcher(from, to, matcher);
}
public static void main(String[] args) {
List<Integer> ints = Arrays.asList(new Integer[] {1,1,1,2,2,2,3,3,3});
assertThat(ints, hasRange(0,2, hasItem(1)));
}
}

my assertThat line does not compile,  and I cannot add a generic type to the new HasRangeMatcher I return in my factory method.

Can someone throw this into eclipse and check it out?

Nat Pryce

unread,
Nov 7, 2010, 5:07:46 AM11/7/10
to hamcre...@googlegroups.com
Which version of Hamcrest are you using?
Reply all
Reply to author
Forward
0 new messages