Как создать утверждение / сопоставление для настраиваемого домена в spock или hamcrest

Я пытаюсь написать assert / matcher, связанный с настраиваемым доменом, в spock или hamcrest, но я не уверен, что делать дальше.

Я попытался написать собственный Matcher на hamcrest, но пока это привело меня только к частичному решению.

Я ищу некоторые рекомендации относительно правильного курса в этом сценарии.

Объекты домена:

  • ResultMap имеет объект Map ‹ILine, IResult> - с каждой ILine связан один INodeResult.
  • IResult имеет 4 (Google Guava) объекта с несколькими картами, которые необходимо проверить.

В моем тесте на спок я хотел бы сделать что-то вроде:

expect:
  that actualResultMap, matchesInAnyOrder(expectedResultMap)
  or
  that actualResultMap, matches(expectedResultMap) // Will only match if everything is in the same order.

Затем внутренний код будет оценивать каждую запись, а также проводить соответствующие тесты для внутренних объектов.

Пока мне удалось написать часть кода, которая оценивает один набор мульти-карт, но я не уверен, как связать свои тесты.

Пользовательское сопоставление:

package com.ps.DE.Test.CustomMatcher

import org.hamcrest.BaseMatcher

class MultimapMatcher {

    /**
     * Checks all the entries in a Multimap with another
     * @param expected
     * @return Shows the failure only if the entries do not match or are not in the same order
     */
    static hasAllInOrder(final com.google.common.collect.Multimap expected){
        [
                matches: { actual ->
                    for(key in actual.keySet()){
                        if (actual.get(key) != expected.get(key)){
                            return false
                        }
                    }
                    return true
                },
                describeTo: { description ->
                    description.appendText("MultiMap entries to be ${expected}")
                },
                describeMismatch: { actual, description ->
                    description.appendText("were ${actual}")
                }
        ]   as BaseMatcher
    }

    /**
     * Checks all the entries in a Multimap with another
     * @param expected
     * @return Shows the failure only if the entries do not match
     */
    static hasAllInAnyOrder(final com.google.common.collect.Multimap expected){
        [
                matches: { actual ->
                    for(key in actual.keySet()){
                        if (!actual.get(key).containsAll(expected.get(key))) {
                            return false
                        }
                    }
                    return true
                },
                describeTo: { description ->
                    description.appendText("MultiMap entries to be ${expected}")
                },
                describeMismatch: { actual, description ->
                    description.appendText("were ${actual}")
                }
        ]   as BaseMatcher
    }
}

Тестирование настраиваемого сопоставителя:

package com.ps.DE.Test.CustomMatcher

import com.google.common.collect.ArrayListMultimap
import com.google.common.collect.Multimap
import spock.lang.Specification

import static com.ps.DE.Test.CustomMatcher.MultimapMatcher.hasAllInAnyOrder
import static com.ps.DE.Test.CustomMatcher.MultimapMatcher.hasAllInOrder
import static org.hamcrest.Matchers.not
import static spock.util.matcher.HamcrestSupport.that


class MultimapMatcherSpec extends Specification {

    def "Test hasAllInOrder"() {
        def actual = ArrayListMultimap.create();

        // Adding some key/value
        actual.put "Fruits", "Apple"
        actual.put "Fruits", "Banana"
        actual.put "Fruits", "Pear"
        actual.put "Vegetables", "Carrot"

        Multimap<String, String> expected = ArrayListMultimap.create();

        // Adding some key/value
        expected.put("Fruits", "Apple");
        expected.put("Fruits", "Banana");
        expected.put("Fruits", "Pear");
        expected.put("Vegetables", "Carrot");

        expect:

        that actual, hasAllInAnyOrder(expected)
        that actual, hasAllInOrder(expected)
    }

    def "Test hasAllInAnyOrder"() {
        Multimap<String, String> actual = ArrayListMultimap.create();

        // Adding some key/value
        actual.put("Fruits", "Apple");
        actual.put("Fruits", "Banana");
        actual.put("Fruits", "Pear");
        actual.put("Vegetables", "Carrot");

        Multimap<String, String> expected = ArrayListMultimap.create();

        // Adding some key/value
        expected.put("Fruits", "Banana");
        expected.put("Fruits", "Apple");
        expected.put("Fruits", "Pear");
        expected.put("Vegetables", "Carrot");

        expect:
        that actual, hasAllInAnyOrder(expected)
        that actual, not(hasAllInOrder(expected))
    }
}

Мы будем благодарны за любую помощь или руководство.


person Pranav Shah    schedule 26.06.2014    source источник
comment
Где ты застрял? Ваши текущие сопоставители работают? Предложение: примите метод фабрики сопоставлений для внутреннего создания сопоставителей, которые будут применяться к парам записей. Это сделало бы средство сопоставления MultiMap более универсальным и позволило бы повторно использовать существующие сопоставители списков без создания новых подклассов.   -  person David Harkness    schedule 27.06.2014
comment
@DavidHarkness Спасибо за предложение. Не могли бы вы указать мне пример, чтобы сделать это правильно?   -  person Pranav Shah    schedule 27.06.2014
comment
Я бы посмотрел исходный код Hamcrest для класса CoreMatchers. Очень поучительно.   -  person BalRog    schedule 17.10.2014


Ответы (1)


Зачем вам вообще нужны кастомные сопоставители? Возможно, Spock и Groovy достаточно мощны, чтобы удовлетворить ваши потребности без специальных сопоставителей.

Чтобы сопоставить два Multimap объекта с одинаковым содержимым в одном и том же порядке, достаточно сделать:

expected:
actual == expected

Есть ли польза от добавления большего количества кода к тому же утверждению?

Для сопоставления в любом порядке это немного сложнее, но достаточно добавить метод сортировки (может быть извлечен и повторно использован в других тестовых классах). Это могло выглядеть так:

expected:
sortValues(actual) == sortValues(expected)

и сам метод:

static Map<String, Collection<String>> sortValues(Multimap<String, String> multimap) {
    multimap.asMap().collectEntries {
        [it.key, it.value.sort(false)]
    }
}

Возможно, для лучшей читаемости спецификации sortValues() может иметь имя anyOrder(), из-за которого утверждение будет выглядеть так:

expect:
anyOrder(actual) == anyOrder(expected)

Затем внутренний код будет оценивать каждую запись, а также выполнять соответствующие тесты для внутренних объектов.

Эта часть может быть реализована методом equals на каждом сравниваемом типе объекта.

person topr    schedule 25.06.2015