Как использовать Hamcrest для проверки элементов карты

Недавно я использовал библиотеку Hamcrest для написания тестов и довольно успешную, но теперь мне нужно сделать что-то более сложное и начать видеть много трудностей. Мне нужно inpsect и проверить свойства элементов на карте. Мой производственный код выглядит примерно так:

    Map<String, List<MyItem>> map = new HashMap<String, List<MyItem>>();
    map.put("one", Arrays.asList(new MyItem("One")));
    map.put("two",  Arrays.asList(new MyItem("Two")));
    map.put("three",  Arrays.asList(new MyItem("Three")));

Я хочу написать несколько тестовых кодов, подобных следующим, но он не компилируется. Похоже, что Hamcrest hasEntry имеет параметризацию по типу, а hasItem и hasProperty только ожидают Object.

    assertThat(map, Matchers.<String, List<MyItem>>hasEntry("one",  hasItem(hasProperty("name", is("One")))));

В моей среде IDE (Eclipse) появляется следующее сообщение об ошибке: Параметрированный метод <String, List<HamcrestTest.MyItem>>hasEntry(String, List<HamcrestTest.MyItem>) типа Matchers неприменим для аргументов (String, Matcher<Iterable<? super Object>>). С одной стороны, я думаю, что Eclipse запутался в том, какой метод hasEntry я хотел использовать, он должен быть hasEntry(org.hamcrest.Matcher<? super K> keyMatcher, org.hamcrest.Matcher<? super V> valueMatcher), а не hasEntry(K key, V value).

Должен ли я просто сдаться и получить предмет с Карты и вручную проверить каждое свойство? Есть ли более чистый способ?

Ответы

Ответ 1

Вы можете просто использовать contains или containsInAnyOrder. Правда, вам придется перечислить все элементы в List таким образом, но он работает более чисто, чем hasItem:

@SuppressWarnings("unchecked")
@Test
public void mapTest() {
  Map<String, List<MyItem>> map = new HashMap<String, List<MyItem>>();
  map.put("one", asList(new MyItem("1"), new MyItem("one")));

  assertThat(map, hasEntry(is("one"),
                           containsInAnyOrder(hasProperty("name", is("one")),
                                              hasProperty("name", is("1")))));
}

Ответ 2

Чтобы просто сделать это проще, вы можете попробовать это...

assertThat((Object)map, (Matcher)Matchers.hasEntry("one",  hasItem(hasProperty("name", is("One")))));

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

Кроме того, вы можете использовать ItIterableContainingInOrder.containingInOrder(new MyItem("One"))). Это проверит весь список, и если MyItem реализует equals, вы не будете использовать отражение в своих тестах.

Ответ 3

Так как @t0mppa не предоставил хороший пример того, как использовать Hamcrest contains и containsInAnyOrder для этого, вот немного чего начать:

Map<Integer, String> columns = new HashMap<Integer, String>();
columns.put(1, "ID");
columns.put(2, "Title");
columns.put(3, "Description");

assertThat(columns.values(), contains("ID", "Title", "Description")); // passes
assertThat(columns.values(), contains("ID", "Description", "Title")); // fails
assertThat(columns.values(), containsInAnyOrder("ID", "Description", "Title")); // passes

Обратите внимание, что в отличие от hasItem и hasItems, они будут работать, только если вы предоставите им полный список всех значений, с которыми вы будете сопоставлять. Подробнее см. Hamcrest javadocs.