Skip to content

Commit 31a92e1

Browse files
Add ListsMatching, ClassCompositeMatcher
1 parent 476228a commit 31a92e1

File tree

7 files changed

+704
-2
lines changed

7 files changed

+704
-2
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ can be used to verify the contents of arrays or Iterators.
100100
* To match an Iterable, use the [`ListsMembers`](http://www.cornutum.org/hamcrest-composites/apidocs/org/cornutum/hamcrest/ListsMembers.html) matcher.
101101
* To match an array, use the [`ListsElements`](http://www.cornutum.org/hamcrest-composites/apidocs/org/cornutum/hamcrest/ListsElements.html) matcher.
102102
* To match an Iterator, use the [`VisitsList`](http://www.cornutum.org/hamcrest-composites/apidocs/org/cornutum/hamcrest/VisitsList.html) matcher.
103+
* Without using `equals`, use the [`ListsMatching`](http://www.cornutum.org/hamcrest-composites/apidocs/org/cornutum/hamcrest/ListsMatching.html) matcher.
103104
* Even if the expected or matched sequence may be `null`? No problem!
104105
* And also compare individual members using a composite matcher? No problem!
105106

@@ -114,6 +115,7 @@ For more examples of how to use composite matchers, see the unit tests for:
114115
* [`ContainsMembers`](src/test/java/org/cornutum/hamcrest/ContainsMembersTest.java)
115116
* [`ListsElements`](src/test/java/org/cornutum/hamcrest/ListsElementsTest.java)
116117
* [`ListsMembers`](src/test/java/org/cornutum/hamcrest/ListsMembersTest.java)
118+
* [`ListsMatching`](src/test/java/org/cornutum/hamcrest/ListsMatchingTest.java)
117119
* [`MatchesFunction`](src/test/java/org/cornutum/hamcrest/MatchesFunctionTest.java)
118120
* [`VisitsList`](src/test/java/org/cornutum/hamcrest/VisitsListTest.java)
119121
* [`VisitsMembers`](src/test/java/org/cornutum/hamcrest/VisitsMembersTest.java)

src/main/java/org/cornutum/hamcrest/BaseCompositeMatcher.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ public abstract class BaseCompositeMatcher<T> extends BaseMatcher<T>
3737
private class CompositeMatcher
3838
{
3939
private final Object matched;
40-
private final Matcher<? super T> mismatch;
40+
private Matcher<? super T> mismatch;
4141

4242
/**
4343
* Creates a new CompositeMatcher instance.
@@ -54,7 +54,7 @@ public CompositeMatcher( Object object)
5454
? new IsNull<T>()
5555
: new IsNot<T>( new IsNull<T>());
5656
}
57-
else
57+
else if( (mismatch = getTypeMismatch( object)) == null)
5858
{
5959
T actual = (T) object;
6060
mismatch =
@@ -112,6 +112,16 @@ public void describeMismatch( Object actual, Description description)
112112
getCompositeMatcher( actual).getMismatch().ifPresent( m -> m.describeMismatch( actual, description));
113113
}
114114

115+
/**
116+
* If the given object is incompatible with the expected type, returns a Matcher that describes the mismatch.
117+
* Otherwise, return null;
118+
*/
119+
protected Matcher<? super T> getTypeMismatch( Object object)
120+
{
121+
// By default, the expected type is undefined. Successful cast of the given object is assumed.
122+
return null;
123+
}
124+
115125
/**
116126
* Adds the Matcher supplied for the expected object to the matchers applied by this Matcher.
117127
*/
@@ -181,6 +191,15 @@ protected static <T,S extends Iterable<T>> ListsMembers.Supplier<T,S> listsMembe
181191
return new ListsMembers.Supplier<>( memberMatcherSupplier);
182192
}
183193

194+
/**
195+
* Returns a new {@link ListsMatching.Supplier} that supplies a {@link ListsMatching} matcher using
196+
* the given member Matcher supplier.
197+
*/
198+
protected static <T,S extends Iterable<T>> ListsMatching.Supplier<T,S> listsMatching( Function<T,Matcher<T>> memberMatcherSupplier)
199+
{
200+
return new ListsMatching.Supplier<>( memberMatcherSupplier);
201+
}
202+
184203
/**
185204
* Returns a new {@link ListsElements.Supplier} that supplies a {@link ListsElements} matcher using
186205
* the given element Matcher supplier.
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//////////////////////////////////////////////////////////////////////////////
2+
//
3+
// Copyright 2020, Cornutum Project
4+
// www.cornutum.org
5+
//
6+
//////////////////////////////////////////////////////////////////////////////
7+
8+
package org.cornutum.hamcrest;
9+
10+
import org.hamcrest.Matcher;
11+
import org.hamcrest.core.IsInstanceOf;
12+
13+
/**
14+
* Base class for a composite matcher the verifies the expected type of a matched object.
15+
*/
16+
public abstract class ClassCompositeMatcher<T> extends BaseCompositeMatcher<T>
17+
{
18+
/**
19+
* Creates a new ClassCompositeMatcher instance.
20+
*/
21+
protected ClassCompositeMatcher( Class<? extends T> expectedType, T expected)
22+
{
23+
super( expected);
24+
setExpectedType( expectedType);
25+
}
26+
27+
/**
28+
* Changes the expected type for this matcher.
29+
*/
30+
protected void setExpectedType( Class<? extends T> expectedType)
31+
{
32+
this.expectedType = expectedType;
33+
}
34+
35+
/**
36+
* Returns the expected type for this matcher.
37+
*/
38+
protected Class<? extends T> getExpectedType()
39+
{
40+
return expectedType;
41+
}
42+
43+
/**
44+
* If the given object is incompatible with the expected type, returns a Matcher that describes the mismatch.
45+
* Otherwise, return null;
46+
*/
47+
protected Matcher<? super T> getTypeMismatch( Object object)
48+
{
49+
return
50+
object != null && getExpectedType().isAssignableFrom( object.getClass())
51+
? null
52+
: new IsInstanceOf( getExpectedType());
53+
}
54+
55+
private Class<? extends T> expectedType;
56+
}

src/main/java/org/cornutum/hamcrest/Composites.java

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,38 @@ public static <T> Matcher<Iterator<T>> visitsList( Function<T,Matcher<T>> member
374374
return new VisitsList<T>( expected, memberMatcherSupplier);
375375
}
376376

377+
/**
378+
* Returns a Matcher for an Iterable containing the given sequence of members (in order).
379+
* Each member of a matched Iterable must satisfy the Matcher returned
380+
* by the given supplier for its counterpart in the given expected Iterable.
381+
*/
382+
public static <T> Matcher<Iterable<T>> listsMatching( Function<T,Matcher<T>> memberMatcherSupplier, Iterable<? extends T> expected)
383+
{
384+
return new ListsMatching<T>( expected, memberMatcherSupplier);
385+
}
386+
387+
/**
388+
* Returns a Matcher for an Iterable containing the given sequence of members (in order).
389+
* Each member of a matched Iterable must satisfy the Matcher returned
390+
* by the given supplier for its counterpart in the given expected Iterable.
391+
*/
392+
@SafeVarargs
393+
public static <T> Matcher<Iterable<T>> listsMatching( Function<T,Matcher<T>> memberMatcherSupplier, T... expected)
394+
{
395+
return listsMatching( memberMatcherSupplier, Arrays.asList( expected));
396+
}
397+
398+
/**
399+
* Returns a Matcher for an Iterable containing the given sequence of members (in order).
400+
* Each member of a matched Iterable must satisfy the Matcher returned
401+
* by the given supplier for its counterpart in the given expected Iterable.
402+
*/
403+
public static <T> Matcher<Iterable<T>> listsMatching( Function<T,Matcher<T>> memberMatcherSupplier, Iterator<T> expected)
404+
{
405+
Iterable<T> iterable = () -> expected;
406+
return listsMatching( memberMatcherSupplier, iterable);
407+
}
408+
377409
/**
378410
* Returns a Matcher that compares values of the given function, using a result Matcher returned by the given supplier.
379411
*/

0 commit comments

Comments
 (0)