如何在没有equals方法的情况下在两个类上声明相等性?
说我有一个没有equals()方法的类,没有源代码。 我想在这个类的两个实例上声明平等。
我可以做多个断言:
assertEquals(obj1.getFieldA(), obj2.getFieldA()); assertEquals(obj1.getFieldB(), obj2.getFieldB()); assertEquals(obj1.getFieldC(), obj2.getFieldC()); ...
我不喜欢这个解决scheme,因为如果一个早期的断言失败,我没有得到完整的等式图片。
我可以手动比较我自己并跟踪结果:
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()不会告诉我哪个字段失败了。
有没有更好的做法,得到我想要的对象,没有inheritance和重写equals(唉)?
Mockito提供reflection匹配器:
Assert.assertThat(expected, new ReflectionEquals(actual, excludeFields));
我通常使用org.apache.commons.lang3.builder.EqualsBuilder来实现这个用例
Assert.assertTrue(EqualsBuilder.reflectionEquals(expected,actual));
我知道这有点旧,但我希望它有帮助。
我遇到了和你一样的问题,所以经过调查,我发现了几个类似的问题,find解决scheme后,我也回答了同样的问题,因为我认为它可以帮助别人。
这个类似问题的 答案最多 (不是作者挑选的答案 ),是最适合你的解决scheme。
基本上,它使用名为Unitils的库。
这是用途:
User user1 = new User(1, "John", "Doe"); User user2 = new User(1, "John", "Doe"); assertReflectionEquals(user1, user2);
即使类User
没有实现equals()
也会传递。 你可以在他们的教程中看到更多的例子和一个叫assertLenientEquals
的非常酷的断言。
库Hamcrest 1.3实用程序匹配器有一个使用reflection而不是平等的特殊匹配器。
assertThat(obj1, reflectEquals(obj2));
这里有很多正确的答案,但我也想添加我的版本。 这是基于Assertj。
import static org.assertj.core.api.Assertions.assertThat; public class TestClass { public void test() { // do the actual test assertThat(actualObject) .isEqualToComparingFieldByFieldRecursively(expectedObject); } }
您可以使用Apache Commons lang ReflectionToStringBuilder
您可以指定要逐个testing的属性,或者更好地排除那些您不想要的属性:
String s = new ReflectionToStringBuilder(o, ToStringStyle.SHORT_PREFIX_STYLE) .setExcludeFieldNames(new String[] { "foo", "bar" }).toString()
然后,你比较这两个string正常。 对于反思速度慢的问题,我认为这只是为了testing,所以不应该那么重要。
按字段比较:
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);
你可以把你发布的比较代码放到一些静态工具的方法中吗?
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));
这不是笨重的,如果它们存在(通过不完全正常的assertEqualforms,但你得到的所有信息,所以它应该是好的),给你充分的信息差异。
您可以使用reflection来“自动化”完整的相等testing。 您可以实现为单个字段编写的相等“跟踪”代码,然后使用reflection在对象的所有字段上运行该testing。
这是一个通用的比较方法,它比较同一类的两个对象的值,它们的值(记住可以通过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 ; } }
我偶然发现了一个非常相似的情况。
我想比较一个对象与另一个对象具有相同的属性值,但像is()
, refEq()
等方法不会像我的对象在其id
属性中有一个空值的原因。
所以这是我find的解决scheme(呃,同事find的):
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,它们在某些属性上有所不同。
在使用AssertJ的情况下,您可以创build自定义比较策略:
assertThat(frodo).usingComparator(raceComparator).isEqualTo(sam) assertThat(fellowshipOfTheRing).usingElementComparator(raceComparator).contains(sauron);
在断言中使用自定义比较策略
断言例子
这不会帮助OP,但它可以帮助任何最终在这里的C#开发人员…
和Enrique一样,你应该重写equals方法。
有没有更好的做法,得到我想要的对象,没有inheritance和重写equals(唉)?
我的build议是不使用子类。 使用部分类。
部分类定义(MSDN)
所以你的class级看起来像…
public partial class TheClass { public override bool Equals(Object obj) { // your implementation here } }
对于Java,我会同意使用reflection的build议。 只要记住,你应该尽可能避免使用reflection。 它很慢,很难debugging,甚至更难保持未来,因为IDE可能会通过做一个字段重命名或类似的东西来破坏你的代码。 小心!
从你的意见到其他答案,我不明白你想要什么。
只是为了讨论,让我们说类没有重写equals方法。
所以你的UT看起来像这样:
SomeType expected = // bla SomeType actual = // bli Assert.assertEquals(expected, actual).
你完成了。 而且,如果断言失败,你不能得到“完全平等的图片”。
根据我的理解,你所说的是,即使这种types没有超越平等,你也不会对它感兴趣,因为你想得到“完全平等的图片”。 所以,延伸和压倒等于没有意义。
所以你必须select:或者比较财产的财产,使用反思或硬编码的检查,我会build议后者。 或者:比较这些对象的人类可读表示 。
例如,您可以创build一个帮助程序类,将您希望比较的types序列化为XML文档,并比较得到的XML! 在这种情况下,你可以直观地看到什么是平等的,什么不是。
这种方法将使您有机会查看完整的图片,但也相对麻烦(起初有一点小错误)。
您可以重写类的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; } }
那么,如果你想比较一个类中的两个对象,你必须以某种方式实现equals和hash代码方法