比较器的Junit Matcher?

Posted

技术标签:

【中文标题】比较器的Junit Matcher?【英文标题】:Junit Matcher for comparators? 【发布时间】:2013-07-30 18:15:43 【问题描述】:

这几天我都在使用 Junit 的 Matchers 功能。一切正常,但我正在寻找一个匹配器,它使用比较器进行比较并且不依赖对象 equals 方法。

我要换

Assert.assertThat(one, CoreMatchers.equalTo(two)

类似(伪代码)

Assert.assertThat(eins, CoreMatchers.equalTo(operand, new MyComparator())

您知道是否存在简单的开箱即用解决方案?我在 google 中没有找到,也不想写一篇。

【问题讨论】:

您可以尝试编写自己的匹配器,扩展BaseMatcher。 很惊讶它不存在。请发布您的解决方案,以便下一个人可以使用它。 【参考方案1】:

Hamcrest 2.0.0.0+ 现在支持此功能。

您可以使用org.hamcrest.comparator.ComparatorMatcherBuilder 类来实现这一点,例如:

ComparatorMatcherBuilder builder = ComparatorMatcherBuilder.comparedBy(equivalenceComparator);
Assert.assertThat(eins, builder.comparesEqualTo(operand));

【讨论】:

此链接已失效。 更正死链接【参考方案2】:

我在 hamcrest 1.3 中遇到了同样的问题,并通过编写一个匹配器来解决它,它遵循 IsEqual-Matcher 的代码,但使用给定的比较器而不是 Object#equals()。

import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.junit.Assert;

import java.lang.reflect.Array;
import java.util.Comparator;

/**
 * Is the value equal to another value, as tested by the
 * given Comparator?<br/>
 * Based on the example of @link org.hamcrest.core.IsEqual.
 *
 * @author Serhat Cinar
 */
public class IsEqualWithComparator<T> extends BaseMatcher<T> 
    private final Object expectedValue;
    private final Comparator<T> comparator;

    public IsEqualWithComparator(T equalArg, Comparator<T> comparator) 
        expectedValue = equalArg;
        this.comparator = comparator;
    

    @Override
    public boolean matches(Object actualValue) 
        return areEqual(actualValue, expectedValue, comparator);
    

    @Override
    public void describeTo(Description description) 
        description.appendValue(expectedValue);
    

    private static boolean areEqual(Object actual, Object expected, Comparator comparator) 
        if (actual == null) 
            return expected == null;
        

        if (expected != null && isArray(actual)) 
            return isArray(expected) && areArraysEqual(actual, expected, comparator);
        

        return comparator.compare(actual, expected) == 0;
    

    private static boolean areArraysEqual(Object actualArray, Object expectedArray, Comparator comparator) 
        return areArrayLengthsEqual(actualArray, expectedArray) && areArrayElementsEqual(actualArray, expectedArray, comparator);
    

    private static boolean areArrayLengthsEqual(Object actualArray, Object expectedArray) 
        return Array.getLength(actualArray) == Array.getLength(expectedArray);
    

    private static boolean areArrayElementsEqual(Object actualArray, Object expectedArray, Comparator comparator) 
        for (int i = 0; i < Array.getLength(actualArray); i++) 
            if (!areEqual(Array.get(actualArray, i), Array.get(expectedArray, i), comparator)) 
                return false;
            
        
        return true;
    

    private static boolean isArray(Object o) 
        return o.getClass().isArray();
    

    @Factory
    public static <T> Matcher<T> equalTo(T operand, Comparator<T> comparator) 
        return new IsEqualWithComparator<>(operand, comparator);
    

    public static void main(String argv[]) 
        Assert.assertThat("abc", IsEqualWithComparator.equalTo("ABC", new Comparator<String>() 
            @Override
            public int compare(String o1, String o2) 
                return o1.equalsIgnoreCase(o2) ? 0 : -1;
            
        ));
    

【讨论】:

【参考方案3】:

另一种选择是将AspectJ framework custom comparison strategy 用于对象和可迭代对象。

assertThat(frodo).usingComparator(raceComparator).isEqualTo(sam);
assertThat(fellowshipOfTheRing).usingElementComparator(raceComparator).contains(sauron);

它也有Field by field comparisons,例如isEqualToComparingOnlyGivenFields 和 isEqualToIgnoringGivenFields 比较器。

assertThat(frodo).isEqualToComparingOnlyGivenFields(sam, "race.name");
assertThat(frodo).isEqualToIgnoringGivenFields(sam, "name", "age");

因此,在大多数情况下,您可以在没有自定义比较策略的情况下处理断言

【讨论】:

【参考方案4】:

我不知道 Hamcrest 有什么东西可以做到这一点。您可能需要编写自定义匹配器。需要考虑的一件事:如果equals 没有返回true,对象是否相等?如果您正在测试特定属性,您的自定义匹配器可能会更清晰地显示为FeatureMatcher (Is there a simple way to match a field using Hamcrest?)。例如,如果测试是针对预期标签的:

assertThat(eins, equalToUnderComparison("1", new LabelComparator());

可能更清楚:

assertThat(eins, hasLabel(eq("1")));

编写自定义匹配器不需要大量代码 (Writing custom matchers),如果它使代码更具可读性,就没有理由避免它。

【讨论】:

这对于 JPA 模型实例来说是非常实际的,您无法根据业务逻辑实现 equals/hashCode。见***.com/questions/5031614/…

以上是关于比较器的Junit Matcher?的主要内容,如果未能解决你的问题,请参考以下文章

JUnit 4 Vs TestNG比较

用StringBuilder和StringBuffer实现的Unicode解码方法的比较(Java)

TestNG 与 Junit的比较

Java两大测试方法Junit和TestNG的比较

在Junit测试中,如何比较两个ArrayList 使用assertEquals(),还有更好的选择吗?

JUnit手动设计测试方法以及与Randoop的自动生成测试的比较