如何宣称平等的两个班没有equals方法?(How do I assert equality on

2019-06-17 21:53发布

说我有没有equals()方法,一类不具有源。 我想断言在该类的两个实例平等。

我可以做多个断言:

assertEquals(obj1.getFieldA(), obj2.getFieldA());
assertEquals(obj1.getFieldB(), obj2.getFieldB());
assertEquals(obj1.getFieldC(), obj2.getFieldC());
...

我不喜欢这样的解决方案,因为如果早断言失败我没有得到完全平等的画面。

我可以手动比较对我自己和跟踪结果:

String errorStr = "";
if(!obj1.getFieldA().equals(obj2.getFieldA())) {
    errorStr += "expected: " + obj1.getFieldA() + ", actual: " + obj2.getFieldA() + "\n";
}
if(!obj1.getFieldB().equals(obj2.getFieldB())) {
    errorStr += "expected: " + obj1.getFieldB() + ", actual: " + obj2.getFieldB() + "\n";
}
...
assertEquals("", errorStr);

这给了我完全平等的画面,但笨重(而且我还没有占到可能为空的问题)。 第三个选择是使用比较,但的compareTo()不会告诉我哪些字段失败平等。

有没有更好的做法,得到了我从对象想要的,没有子和overridding的equals(啊)?

Answer 1:

的Mockito提供反射匹配:

Assert.assertThat(expected, new ReflectionEquals(actual, excludeFields));


Answer 2:

我一般实现使用org.apache.commons.lang3.builder.EqualsBuilder这个用例

Assert.assertTrue(EqualsBuilder.reflectionEquals(expected,actual));


Answer 3:

有许多正确的答案在这里,但我想补充我的版本太多。 这是基于Assertj。

import static org.assertj.core.api.Assertions.assertThat;

public class TestClass {

    public void test() {
        // do the actual test
        assertThat(actualObject)
            .isEqualToComparingFieldByFieldRecursively(expectedObject);
    }
}


Answer 4:

我知道这是一个有点老了,但我希望它能帮助。

我碰到你,所以,经过调查,我发现比这几个类似的问题同样的问题,并找到解决方案后,我回答这些相同的,因为我认为它可以帮助别人。

最投票的答案 (而不是一个由作者挑选)如此类似的问题 ,是你最合适的解决方案。

基本上,它包括使用称为库Unitils 。

这是使用:

User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertReflectionEquals(user1, user2);

即使类将通过User没有实现equals() 。 你可以看到更多的例子和一个非常酷的断言称为assertLenientEquals在他们的教程 。



Answer 5:

您可以使用Apache的公郎ReflectionToStringBuilder

您可以指定你想测试一个接一个,或更好的属性,排除那些你不想要的:

String s = new ReflectionToStringBuilder(o, ToStringStyle.SHORT_PREFIX_STYLE)
                .setExcludeFieldNames(new String[] { "foo", "bar" }).toString()

然后,这两个字符串比较正常。 有关反射是慢了点,我认为这只是测试,所以不应该是如此重要。



Answer 6:

如果您使用hamcrest您的断言(assertThat)和不希望额外的测试库拉,那么你可以使用SamePropertyValuesAs.samePropertyValuesAs断言,没有一个重写的equals方法的项目。

好处是,你不必在另一个测试框架拉当断言失败,它会给出一个有用的错误( expected: field=<value> but was field=<something else> ),而不是expected: true but was false ,如果你使用像EqualsBuilder.reflectionEquals()

缺点是,它是一个比较浅,并有排除领域(如存在EqualsBuilder)没有选项,所以你必须解决嵌套的对象(例如,删除它们,并将它们分别比较)。

最佳案例:

import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
...
assertThat(actual, is(samePropertyValuesAs(expected)));

丑陋的案例:

import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
...
SomeClass expected = buildExpected(); 
SomeClass actual = sut.doSomething();

NestedClass expectedSubObject = expected.getSubObject();
expected.setSubObject(null);

NestedClass actualSubObject = actual.getSubObject();
actual.setSubObject(null);

assertThat(actual, is(samePropertyValuesAs(expected)));
assertThat(actualSubObject, is(samePropertyValuesAs(expectedSubObject)));

所以,挑选你的毒药。 额外的框架(例如Unitils),无益的错误(例如EqualsBuilder)或浅比较(hamcrest)。



Answer 7:

图书馆Hamcrest 1.3实用匹配器具有使用反射,而不是平等的特殊匹配。

assertThat(obj1, reflectEquals(obj2));


Answer 8:

比较场逐场:

assertNotNull("Object 1 is null", obj1);
assertNotNull("Object 2 is null", obj2);
assertEquals("Field A differs", obj1.getFieldA(), obj2.getFieldA());
assertEquals("Field B differs", obj1.getFieldB(), obj2.getFieldB());
...
assertEquals("Objects are not equal.", obj1, obj2);


Answer 9:

您可以使用反射来“自动化”的完全平等的测试。 你可以实现你一个字段写的平等“跟踪”代码,然后使用反射来运行对象中的所有字段的考验。



Answer 10:

这是一个比较通用的方法,即在同一类的两个对象为它的值进行比较的字段(记住,这些都是通过GET方法访问)

public static <T> void compare(T a, T b) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    AssertionError error = null;
    Class A = a.getClass();
    Class B = a.getClass();
    for (Method mA : A.getDeclaredMethods()) {
        if (mA.getName().startsWith("get")) {
            Method mB = B.getMethod(mA.getName(),null );
            try {
                Assert.assertEquals("Not Matched = ",mA.invoke(a),mB.invoke(b));
            }catch (AssertionError e){
                if(error==null){
                    error = new AssertionError(e);
                }
                else {
                    error.addSuppressed(e);
                }
            }
        }
    }
    if(error!=null){
        throw error ;
    }
}


Answer 11:

我偶然在一个非常类似的情况。

我希望能在一个测试比较,一个对象有相同的属性值作为一个又一个,但喜欢的方法is() refEq()等也不会像在其空值我对象的原因工作id属性。

因此,这是我发现(当然,一个同事发现)的解决方案:

import static org.apache.commons.lang.builder.CompareToBuilder.reflectionCompare;

assertThat(reflectionCompare(expectedObject, actualObject, new String[]{"fields","to","be","excluded"}), is(0));

如果从所获得的值reflectionCompare是0,这意味着它们是相等的。 如果是-1或1,他们在某些属性不同。



Answer 12:

在与AssertJ通常情况下,你可以创建自定义的比较策略:

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

使用断言自定义比较策略

AssertJ例子



Answer 13:

一些反射的比较方法是浅

另一种选择是将对象转换为一个JSON和比较字符串。

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;    
public static String getJsonString(Object obj) {
 try {
    ObjectMapper objectMapper = new ObjectMapper();
    return bjectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj);
     } catch (JsonProcessingException e) {
        LOGGER.error("Error parsing log entry", e);
        return null;
    }
}
...
assertEquals(getJsonString(MyexpectedObject), getJsonString(MyActualObject))


Answer 14:

我有相同的难题时,单元测试的Android应用程序,我想出了一个最简单的方法是简单地使用GSON到我的实际和预期值对象转换成json并比较它们为字符串。

String actual = new Gson().toJson( myObj.getValues() );
String expected = new Gson().toJson( new MyValues(true,1) );

assertEquals(expected, actual);

该上手动比较场逐场的优点是,你比较所有领域,因此,即使你以后一个新的领域上添加到您的类,将自动进行测试,相比于如果你使用的是一堆assertEquals()上的所有字段,然后这将需要更新,如果你添加更多的字段上您的课。

JUnit的还会显示您的字符串,这样就可以直接看到它们之间的区别。 不知道通过现场订货如何可靠Gson是,虽然,这可能是一个潜在的问题。



Answer 15:

使用Shazamcast ,你可以这样做:

assertThat(obj1, sameBeanAs(obj2));


Answer 16:

我尝试了所有的答案,并没有真正为我工作。

所以,我创建了自己的方法,该方法的简单Java对象,而不深入到嵌套结构比较...

如果所有字段匹配或不匹配包含字符串信息的方法返回null。

只有有一个getter方法的属性进行比较。

如何使用

        assertNull(TestUtils.diff(obj1,obj2,ignore_field1, ignore_field2));

示例输出,如果存在不匹配

输出显示属性名称和比较的对象的各个值

alert_id(1:2), city(Moscow:London)

代码(爪哇8及以上):

 public static String diff(Object x1, Object x2, String ... ignored) throws Exception{
        final StringBuilder response = new StringBuilder();
        for (Method m:Arrays.stream(x1.getClass().getMethods()).filter(m->m.getName().startsWith("get")
        && m.getParameterCount()==0).collect(toList())){

            final String field = m.getName().substring(3).toLowerCase();
            if (Arrays.stream(ignored).map(x->x.toLowerCase()).noneMatch(ignoredField->ignoredField.equals(field))){
                Object v1 = m.invoke(x1);
                Object v2 = m.invoke(x2);
                if ( (v1!=null && !v1.equals(v2)) || (v2!=null && !v2.equals(v1))){
                    response.append(field).append("(").append(v1).append(":").append(v2).append(")").append(", ");
                }
            }
        }
        return response.length()==0?null:response.substring(0,response.length()-2);
    }


Answer 17:

你可以把你贴到一些静态工具方法的对比代码?

public static String findDifference(Type obj1, Type obj2) {
    String difference = "";
    if (obj1.getFieldA() == null && obj2.getFieldA() != null
            || !obj1.getFieldA().equals(obj2.getFieldA())) {
        difference += "Difference at field A:" + "obj1 - "
                + obj1.getFieldA() + ", obj2 - " + obj2.getFieldA();
    }
    if (obj1.getFieldB() == null && obj2.getFieldB() != null
            || !obj1.getFieldB().equals(obj2.getFieldB())) {
        difference += "Difference at field B:" + "obj1 - "
                + obj1.getFieldB() + ", obj2 - " + obj2.getFieldB();
        // (...)
    }
    return difference;
}

比你可以在这样的JUnit使用此方法:

的assertEquals( “对象不相等”, “”,findDifferences(OBJ1,OBJ));

这是不笨重,为您提供有关差异的完整信息,如果存在的话(通过不完全在assertEqual便的正常形态,但你得到的所有信息,因此应该是不错的)。



Answer 18:

这将不利于OP,但它可能会帮助谁在这里结束的任何C#开发...

像恩里克贴 ,你应该重写equals方法。

有没有更好的做法,得到了我从对象想要的,没有子和overridding的equals(啊)?

我的建议是不要使用一个子类。 使用部分类。

局部类定义(MSDN)

所以,你的类会是什么样子?

public partial class TheClass
{
    public override bool Equals(Object obj)
    {
        // your implementation here
    }
}

对于Java,我会建议同意使用反射。 只要记住,你应该避免使用反射只要有可能。 它是缓慢的,难以调试,甚至难以维持到未来,因为IDE中可以通过做一个字段重命名或类似的东西,破坏你的代码。 小心!



Answer 19:

从您的意见其他答案,我不明白你想要什么。

只是为了讨论的方便,可以说的是,这个类没有覆盖equals方法。

所以,你的UT看起来是这样的:

SomeType expected = // bla
SomeType actual = // bli

Assert.assertEquals(expected, actual). 

和你做。 此外,你不能得到“完全平等的画面”如果断言失败。

据我了解,你是说,即使没有类型覆盖等于,你就不会感兴趣,因为你想获得“完全平等的画面”。 所以在扩大和压倒一切没有意义等于无论是。

所以,你必须选择:由产权或者比较属性,使用反射或硬编码的检查,我建议后者。 或者:比较这些对象的人类可读表示

例如,您可以创建一个序列化你希望tocompare XML文档和比比较生成的XML类型的辅助类! 在这种情况下,你可以直观地看到究竟是相等的,哪些不是。

这种方法会给你机会看到全貌,但它也是比较繁琐(起初有点容易出错)。



Answer 20:

您可以覆盖类的equals方法,如:

@Override
public int hashCode() {
    int hash = 0;
    hash += (app != null ? app.hashCode() : 0);
    return hash;
}

@Override
public boolean equals(Object object) {
    HubRule other = (HubRule) object;

    if (this.app.equals(other.app)) {
        boolean operatorHubList = false;

        if (other.operator != null ? this.operator != null ? this.operator
                .equals(other.operator) : false : true) {
            operatorHubList = true;
        }

        if (operatorHubList) {
            return true;
        } else {
            return false;
        }
    } else {
        return false;
    }
}

好吧,如果你想从一类比较两个对象,你必须以某种方式实现平等和散列码方法



文章来源: How do I assert equality on two classes without an equals method?