Hamcrest:如何为匹配器实例化和投射?

yan*_*kee 16 java hamcrest

假设以下简单测试:

@Test
public void test() throws Exception {
    Object value = 1;
    assertThat(value, greaterThan(0));
}
Run Code Online (Sandbox Code Playgroud)

测试不会编译,因为"greaterThan"只能应用于类型的实例Comparable.但我想断言这value是一个大于零的整数.我怎样才能用Hamcrest来表达呢?

到目前为止我尝试了什么:

简单的解决方案是通过强制转换匹配器来删除泛型:

assertThat(value, (Matcher)greaterThan(0));
Run Code Online (Sandbox Code Playgroud)

可能,但生成编译器警告并感觉不对.

一个冗长的选择是:

@Test
public void testName() throws Exception {
    Object value = 1;

    assertThat(value, instanceOfAnd(Integer.class, greaterThan(0)));
}

private static<T> Matcher<Object> instanceOfAnd(final Class<T> clazz, final Matcher<? extends T> submatcher) {
    return new BaseMatcher<Object>() {
        @Override
        public boolean matches(final Object item) {
            return clazz.isInstance(item) && submatcher.matches(clazz.cast(item));
        }

        @Override
        public void describeTo(final Description description) {
            description
                .appendText("is instanceof ")
                .appendValue(clazz)
                .appendText(" and ")
                .appendDescriptionOf(submatcher);
        }

        @Override
        public void describeMismatch(final Object item, final Description description) {
            if (clazz.isInstance(item)) {
                submatcher.describeMismatch(item, description);
            } else {
                description
                    .appendText("instanceof ")
                    .appendValue(item == null ? null : item.getClass());
            }
        }
    };
}
Run Code Online (Sandbox Code Playgroud)

感觉"整洁"和"正确",但实际上很多代码看起来很简单.我试图在hamcrest找到类似内置的东西,但我没有成功,但也许我错过了什么?

背景

在我的实际测试用例中,代码如下:

Map<String, Object> map = executeMethodUnderTest();
assertThat(map, hasEntry(equalTo("the number"), greaterThan(0)));
Run Code Online (Sandbox Code Playgroud)

在我的问题的简化案例中,我也可以写assertThat((Integer)value, greaterThan(0)).在我的实际案例中,我可以写assertThat((Integer)map.get("the number"), greaterThan(0)));,但如果出现问题,那当然会使错误信息恶化.

Mag*_*lex 7

这个答案不会显示如何使用Hamcrest进行此操作,我不知道是否有比建议的方法更好的方法。

但是,如果您有可能包含另一个测试库,则AssertJ完全支持此功能:

import org.junit.Test;

import static org.assertj.core.api.Assertions.assertThat;

public class TestClass {

  @Test
  public void test() throws Exception {
    Object value = 1;
    assertThat(value).isInstanceOfSatisfying(Integer.class, integer -> assertThat(integer).isGreaterThan(0));
  }

}
Run Code Online (Sandbox Code Playgroud)

不需要任何转换,AssertJ会为您完成此操作。

另外,如果断言失败,它会输出一个漂亮的错误消息,该消息value太小:

java.lang.AssertionError:
Expecting:
 <0>
to be greater than:
 <0> 
Run Code Online (Sandbox Code Playgroud)

或如果value不是正确的类型:

java.lang.AssertionError: 
Expecting:
 <"not an integer">
to be an instance of:
 <java.lang.Integer>
but was instance of:
 <java.lang.String>
Run Code Online (Sandbox Code Playgroud)

isInstanceOfSatisfying(Class<T> type, Consumer<T> requirements)可以在此处找到Javadoc ,其中还包含一些更为复杂的断言示例:

// second constructor parameter is the light saber color
Object yoda = new Jedi("Yoda", "Green");
Object luke = new Jedi("Luke Skywalker", "Green");

Consumer<Jedi> jediRequirements = jedi -> {
  assertThat(jedi.getLightSaberColor()).isEqualTo("Green");
  assertThat(jedi.getName()).doesNotContain("Dark");
};

// assertions succeed:
assertThat(yoda).isInstanceOfSatisfying(Jedi.class, jediRequirements);
assertThat(luke).isInstanceOfSatisfying(Jedi.class, jediRequirements);

// assertions fail:
Jedi vader = new Jedi("Vader", "Red");
assertThat(vader).isInstanceOfSatisfying(Jedi.class, jediRequirements);
// not a Jedi !
assertThat("foo").isInstanceOfSatisfying(Jedi.class, jediRequirements);
Run Code Online (Sandbox Code Playgroud)


900*_*000 4

问题是您在这里丢失了类型信息:

 Object value = 1;
Run Code Online (Sandbox Code Playgroud)

如果你仔细想想,这是一句极其奇怪的台词。这value是可能的最通用的事情,没有什么可以合理地告诉它,除了检查它是否是null或检查它的字符串表示形式(如果不是)。我有点不知所措地试图想象现代 Java 中上述行的合法用例。

明显的解决方法是assertThat((Comparable)value, greaterThan(0));

更好的解决方法是强制转换为Integer,因为您正在与整数常量进行比较;字符串也是可比较的,但仅限于它们之间。

如果你不能假设你的value是偶数Comparable,那么将它与任何东西进行比较都是毫无意义的。如果您的测试在转换为 时失败Comparable,那么这是一个有意义的报告,表明您Object从其他内容动态转换为失败。