1

我有不同的值对象,每个都有不同的字段集。如何使用 Hamcrest 匹配器检查这些?

    public class ValueObjectA {
        public Integer field1;
        public String field2;
        public long filed3;
        public Object filed4;
    }

    public class ValueObjectB {
        public String field1;
        public int field2;
    }

这就是我想要做的:

    resultA = getResultA();
    ValueObjectA expectedA = new ValueObjectA();
    expectedA.field1 = 4;
    resultB = getResultB();
    ValueObjectB expectedB = new ValueObjectB();
    expectedB.field1 = "foo";
    assertThat(resultA, new ValueObjectMatcher(expectedA));
    assertThat(resultB, new ValueObjectMatcher(expectedB));

我找到了一个 PropertyMatcher,但它只使用公共 getter。我可以写一些类似的东西,使用反射来获取公共领域。但是有现成的吗?

4

2 回答 2

4

您可以使用我们最近开源的库来执行此操作。它的工作原理与 PropertyMatcher 类似,但旨在进行深度对象图。结果是它在公共领域“正常工作”。

github上的shazamcrest

于 2013-10-14T22:55:21.487 回答
3

无论如何,我基于 PropertyMatcher 写了一些东西,以防有人想为例如编写单元测试。com.j256.ormlite :

public class ValueObjectMatcher extends TypeSafeDiagnosingMatcher {
    private final Object expectedVo;
    private final Set<String> fieldNames;
    private final List<FieldMatcher> fieldMatchers;

    public ValueObjectMatcher(final Object expectedVo) {
        Field[] fieldsToMatch = expectedVo.getClass().getFields();
        this.expectedVo = expectedVo;
        this.fieldNames = fieldNamesFrom(fieldsToMatch);
        this.fieldMatchers = fieldMatchersFor(expectedVo, fieldsToMatch);
    }

    @Override
    protected boolean matchesSafely(final Object item, final Description mismatchDescription) {
        return hasAllFields(item, mismatchDescription) && hasMatchingValues(item, mismatchDescription);
    }

    @Override
    public void describeTo(final Description description) {
        description.appendText("same field values as " + expectedVo.getClass().getSimpleName())
                .appendList(" <", ", ", ">", fieldMatchers);
    }

    private boolean hasMatchingValues(final Object item, final Description mismatchDescription) {
        mismatchDescription.appendText(item + " has <");
        int mismatchCount = 0;

        for (FieldMatcher fieldMatcher : fieldMatchers) {
            if (!fieldMatcher.matches(item)) {
                if (mismatchCount != 0) {
                    mismatchDescription.appendText(", ");
                }
                fieldMatcher.describeMismatch(item, mismatchDescription);
                mismatchCount++;
            }
        }

        mismatchDescription.appendText(">");
        return mismatchCount == 0;
    }

    private boolean hasAllFields(final Object item, final Description mismatchDescription) {
        final Field[] fields = item.getClass().getFields();

        final Set<String> itemsFieldNames = fieldNamesFrom(fields);

        boolean result = true;

        for (String fieldName : fieldNames) {
            if (!itemsFieldNames.contains(fieldName)) {
                result = false;
                mismatchDescription.appendText("missing field: " + fieldName);
            }
        }

        return result;
    }

    private List<FieldMatcher> fieldMatchersFor(final Object expectedVo, final Field[] fields) {
        List<FieldMatcher> result = new ArrayList<FieldMatcher>(fields.length);
        try {
            for (Field field : fields) {
                result.add(new FieldMatcher(field, expectedVo));
            }
        }
        catch (NoSuchFieldException e) {
            throw new IllegalStateException("Programmer exception, pls replace programmer: " +
                                            "field list doesn't match with the fields of the provided expectedVo", e);
        }
        catch (IllegalAccessException e) {
            throw new IllegalStateException("Programmer exception, pls replace programmer: " +
                                            "field list doesn't match with the fields of the provided expectedVo", e);
        }
        return result;
    }

    private Set<String> fieldNamesFrom(final Field[] fieldsToMatch) {
        HashSet<String> result = new HashSet<String>();
        for (Field field : fieldsToMatch) {
            result.add(field.getName());
        }
        return result;
    }

    public class FieldMatcher extends DiagnosingMatcher<Object> {

        private final Object expectedFieldValue;
        private final String fieldName;

        private FieldMatcher(Field field, Object expectedVo) throws NoSuchFieldException, IllegalAccessException {
            this.fieldName = field.getName();
            this.expectedFieldValue = expectedVo.getClass().getField(fieldName).get(expectedVo);
        }

        @Override
        protected boolean matches(final Object item, final Description mismatchDescription) {
            try {
                final Field fieldItem = item.getClass().getField(fieldName);
                final Object fieldObjectItem = fieldItem.get(item);

                if (fieldObjectItem == null) {
                    if (expectedFieldValue != null) {
                        mismatchDescription.appendText(fieldName + ": " + fieldObjectItem);
                    }
                } else if (!fieldObjectItem.equals(expectedFieldValue)) {
                    mismatchDescription.appendText(fieldName + ": " + fieldObjectItem);
                }
            }
            catch (IllegalAccessException e) {
                mismatchDescription.appendText(fieldName + " is inaccessible");
                e.printStackTrace();
            }
            catch (NoSuchFieldException e) {
                mismatchDescription.appendText(fieldName + " doesn't exist");
                e.printStackTrace();
            }

            return false;
        }

        @Override
        public void describeTo(final Description description) {
            description.appendText(fieldName + ": " + expectedFieldValue);
        }
    }
}
于 2013-09-25T19:26:05.967 回答