hamcrest / JavaHamcrest

Java (and original) version of Hamcrest

Home Page:http://hamcrest.org/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Request: Lambda Matcher

Rhobal opened this issue · comments

Create a matcher that takes a lambda as type safe replacement of Matchers.hasProperty(propertyName, valueMatcher)

Sample code:

public class LambdaMatcher<T, S> extends BaseMatcher<T> {
    private final Function<T, S> lambda;
    private final String description;
    private Matcher<S> matcher;

    public LambdaMatcher(Function<T, S> lambda, Matcher<S> matcher,
                         String description) {
        this.lambda = lambda;
	this.matcher = matcher;
        this.description = description;
    }

    public LambdaMatcher(Function<T, S> lambda, Matcher<S> matcher) {
    	this(lambda, matcher, "");
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean matches(Object argument) {
        return matcher.matches(lambda.apply((T) argument));
    }

    @Override
    public void describeTo(Description description) {
        description.appendText(this.description);
    }
}

Usage:

assertThat(list, Matchers.hasItem(allOf(
   new LambdaMatcher<>(it -> it.getStringElem(), is("el1")),
   new LambdaMatcher<>(it -> it.getLongElem(), is(2L)))));

I'd love to get better support for lamdas and other Java 8 features into Hamcrest.

At the moment, Hamcrest 1.x works with Java 5 and greater, Hamcrest 2.x works with Java 7 and greater. For this feature, we'd have to increase the minimum supported version to Java 8.

Issues #206 and #207 discuss various ways we might go about doing that, though we haven't made much progress against those issues yet. I think this feature would have to depend upon those issues being completed first.

Couldn't this be done via an extension? That would also alleviate some of the issues raised in #206 and #207 -> each extension can have their own minimal JDK version

You can try to use hamcrest-more-matchers (available on maven central). Your sample will look like

import static com.github.seregamorph.hamcrest.MoreMatchers.where;

    @Test
    public void test() {
        List<Item> list = Arrays.asList(new Item()
                .setStringElem("el-non-match")
                .setLongElem(2L));

        assertThat(list, hasItem(allOf(
                where(Item::getStringElem, is("el1")),
                where(Item::getLongElem, is(2L)))));
    }

    public static class Item {
        private String stringElem;
        private long longElem;

        public String getStringElem() {
            return stringElem;
        }

        public long getLongElem() {
            return longElem;
        }

        public Item setStringElem(String stringElem) {
            this.stringElem = stringElem;
            return this;
        }

        public Item setLongElem(long longElem) {
            this.longElem = longElem;
            return this;
        }
    }

Please note, the failed message will have convenient diagnostics (method reference resolved as string): after call Item.getStringElem

java.lang.AssertionError: 
Expected: a collection containing (Object that matches is "el1" after call Item.getStringElem and Object that matches is <2L> after call Item.getLongElem)
     but: Object that matches is "el1" after call Item.getStringElem was "el-non-match"

It works not only in Java 8, but also in Java 11 and 14.

Thanks, switched to hamcrest.MoreMatchers.where. Works like a charm