代码之家  ›  专栏  ›  技术社区  ›  ewok

hamcrest:测试列表包含一个具有特定值的私有字段的项

  •  1
  • ewok  · 技术社区  · 6 年前

    我有这门课:

    public class A {
        private int x;
    
        public A(int x) {
            this.x = x;
        }
    }
    

    以及我要测试的其他类中的方法:

    public class B {
        public List underTest() {
            int x = doStuff();
            return Collections.singletonList(new A(x));
        }
    
        private int doStuff() { /* ... */ }
    }
    

    我想测试一下,在 underTest() ,返回列表中的项包含 x 字段等于某个值。我写的是:

    import static org.hamcrest.MatcherAssert.assertThat;
    import static org.hamcrest.Matchers.contains;
    import static org.hamcrest.Matchers.hasProperty;
    import static org.hamcrest.Matchers.is;
    
    @Test
    public void Test1() {
        List result = bInstance.underTest();
        assertThat(result, contains(hasProperty("x", is(1))));
    }
    

    但朱尼特抱怨说 item 0: No Property "x" 对于我的测试用例。

    我如何测试这个?我唯一能想到的就是为 getX() ,然后遍历返回的 List 检查每个元素。记住,当方法返回 singletonList ,返回类型为 ,所以 能够 将来要更改为包含多个值。

    1 回复  |  直到 6 年前
        1
  •  2
  •   Michael    6 年前

    我认为第一个值得说的是,像这样测试类内部不是一个好主意,除非在非常特殊的情况下。您的测试将是脆弱的,通常完全安全的更改(即重命名字段)现在可能会导致自动生成失败。你应该测试 外部行为 ,而不是实现细节。

    看来你最好执行 equals hashCode 在你们班 A ,那么您可以简单地执行以下操作:

    contains(new A(1))
    

    如果你确实有充分的理由这样做(这种情况很少见),那么你就不能使用 hasProperty 为此。

    the JavaDoc :

    创建匹配被检查对象具有JavaBean时的匹配器 具有指定名称的属性,其值满足指定的 匹配器。

    我相信这意味着你需要一个名为 getX .

    您不应该仅仅为了测试的目的而添加这样的方法,但是您可以编写自己的通用方法。 Matcher 将使用反射来检查类字段的实现。

    下面是一个示例实现:

    class ReflectiveFieldMatcher<T> extends BaseMatcher<Object>
    {
        private final String fieldName;
        private final T expectedValue;
    
        ReflectiveFieldMatcher(final String fieldName, final T expectedValue)
        {
            this.fieldName = fieldName;
            this.expectedValue = expectedValue;
        }
    
        @Override
        public boolean matches(final Object obj)
        {
            for (final Field field : obj.getClass().getFields())
            {
                if (field.getName().equals(fieldName))
                {
                    field.setAccessible(true);
                    try
                    {
                        Object value = field.get(obj);
                        return expectedValue.equals(value);
                    }
                    catch (final IllegalAccessException e)
                    {
                        throw new RuntimeException(e);
                    }
                }
            }
            return false;
        }
    
        @Override
        public void describeTo(final Description description)
        {
            description.appendText("Object with field '" + fieldName + "' with value: " + expectedValue);
        }
    }
    

    您的示例如下所示:

    assertThat(result, contains(new ReflectiveFieldMatcher<>("x", 1)));