Hamcrest: тестовый список содержит элемент с закрытым полем с определенным значением - PullRequest
0 голосов
/ 23 января 2019

У меня есть этот класс:

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))));
}

Но junit жалуется, что item 0: No Property "x" для моего теста.

Как я могу это проверить? Единственное, о чем я могу подумать, это добавить общедоступный метод получения для getX(), затем выполнить итерацию возвращенного List и проверить каждый элемент. Помните, что, хотя метод возвращает singletonList, тип возвращаемого значения - просто List, поэтому в будущем можно будет изменить , чтобы в нем было несколько значений.

1 Ответ

0 голосов
/ 23 января 2019

Я думаю, что в первую очередь стоит сказать, что такое тестирование внутренних классов не очень хорошая идея, за исключением, может быть, очень особых случаев. Ваши тесты будут хрупкими, и изменения, которые обычно были бы абсолютно безопасными, т. Е. Переименование поля, теперь могут привести к сбою автоматической сборки. Вы должны проверить внешнее поведение , а не детали реализации.

Кажется, вам лучше реализовать equals и hashCode в вашем классе A, так что вы можете просто сделать:

contains(new A(1))

С учетом сказанного, если у вас есть действительно веская причина для этого (а такие случаи будут редкими), вы не сможете использовать hasProperty для этого.

С 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)));
...