JavaHamcrest icon indicating copy to clipboard operation
JavaHamcrest copied to clipboard

either(...).or(...) broken for null values

Open marksarnold opened this issue 10 years ago • 5 comments

Hello,

please take a look at the below test. I would expect all checks to succeed. However, the second and third do fail with:

java.lang.AssertionError: 
Expected: ("" or null)
     but: was null

The complete test code is this:

package zzz_small_tests;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.core.CombinableMatcher.either;

import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ErrorCollector;

public class HamcrestEitherOrTest {
    @Rule
    public ErrorCollector collector= new ErrorCollector();


    @Test 
    public void testEitherOr() {
        String _s1 = "";
        collector.checkThat(_s1, either(equalTo("")).or(nullValue()));
        String _s2 = null;
        collector.checkThat(_s2, either(equalTo("")).or(nullValue()));
        collector.checkThat(_s2, either(equalTo("")).or(nullValue(String.class)));
    }
}

marksarnold avatar Oct 02 '15 07:10 marksarnold

I guess this addresses the situation as issue #49.

UrsMetz avatar Oct 02 '15 16:10 UrsMetz

True. So I guess, the most sensible workaround for now would be using anyOf(equalTo(""), nullValue())...

If a fix can not be implemented in the next release, then at least the API doc of CombinableEitherMatcher and the respective either() and or() methods should warn about this!?

marksarnold avatar Oct 03 '15 06:10 marksarnold

This is caused by this little pice of code in TypeSafeDiagnosingMatcher

@Override
    @SuppressWarnings("unchecked")
    public final boolean matches(Object item) {
        return item != null
            && expectedType.isInstance(item)
            && matchesSafely((T) item, new Description.NullDescription());
    }

The item != null and expectedType.isInstance(item) is false for item = null

This can be fixed by adding this dirty hack to CombinableMatcher

@Override
  public final boolean matches(Object item) {
    if(this.matcher instanceof AnyOf)
    {
      AnyOf anyOf = (AnyOf) this.matcher;
      List<Matcher<T>> matchers = (List<Matcher<T>>) anyOf.getMatchers();
      Boolean matches = false;
      for(Matcher eachMatcher : matchers)
      {
        if(eachMatcher.matches(item))
        {
          matches = true;
        }
      }
      return matches;
    }
    else {
      return super.matches(item);
    }
  }

Therefore TypeSafeDiagnosingMatcher's matches method must be non final and ShortcutCombination needs a getter for matchers.

I know this is really dirty, maybe someone can make a fix with this information. I'll also try to write a better fix for that.

EDIT: I also wrote a test private static final CombinableMatcher<String> EMPTY_STRING_OR_NULL = CombinableMatcher.either(equalTo("")).or(nullValue());

@Test public void
    acceptNullOrEmptyString() {
        assertMatches("or didn't pass", EMPTY_STRING_OR_NULL, null);
        assertMatches("either didn't pass", EMPTY_STRING_OR_NULL, "");
    }

StarBax89 avatar Sep 28 '16 14:09 StarBax89

Just ran into this issue. It's been 5 years after the initial report or even last comment so I wanted to bring attention to this again. Thank you for the workaround @marksarnold which I was able to use.

ScottG489 avatar Apr 23 '21 01:04 ScottG489

I also ran into this issue today. Has there been any progress on this, given that the issue was raised 5 years ago?

Lms24 avatar Sep 22 '21 11:09 Lms24