【问题标题】:How to create a custom domain specific assert/matcher in spock or hamcrest如何在 spock 或 hamcrest 中创建自定义域特定断言/匹配器
【发布时间】:2014-08-16 21:21:35
【问题描述】:

我正在尝试在 spock 或 hamcrest 中编写与域相关的自定义断言/匹配器,但我不确定如何继续。

我尝试在 hamcrest 中编写自定义 Matcher,但到目前为止,这只是部分解决方案。

我正在寻找有关在这种情况下的正确路线的指导。

领域对象:

  • ResultMap 有一个对象 Map - 有一个 INodeResult 与每个 ILine 关联。
  • IResult 有 4 个 (Google Guava) 多图对象需要验证。

我想在我的 spock 测试中做的事情是这样的:

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

我们将不胜感激任何帮助或指导。

【问题讨论】:

  • 你卡在哪里了?您当前的匹配器是否有效?建议:接受匹配器工厂方法以在内部创建匹配器以应用于条目对。这将使整个 MultiMap 匹配器更加通用,并允许重复使用现有的列表匹配器,而无需创建新的子类。
  • @DavidHarkness 感谢您的建议。你能给我举个例子来正确地做到这一点吗?
  • 我想看看 CoreMatchers 类的 Hamcrest 源代码。很有启发性。

标签: java unit-testing groovy spock hamcrest


【解决方案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方法在每个被比较的对象类型上实现。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多