断言两个java bean是等价的

时间:2009-11-16 09:27:05

标签: java unit-testing assert javabeans equality

This question很接近,但仍然不是我想要的。我想以通用的方式断言两个bean对象是等价的。如果它们不是,我想要一个详细的错误消息来解释差异,而不是布尔“等于”或“不等于”。

12 个答案:

答案 0 :(得分:14)

import static org.hamcrest.beans.SamePropertyValuesAs.samePropertyValuesAs;
import static org.junit.Assert.assertThat;

@Test
public void beansAreTheSame(){
    MyDomianClass bean1 = new MyDomainClass();
    MyDomianClass bean2 = new MyDomainClass();
    //TODO - some more test logic

    assertThat(bean1, samePropertyValuesAs(bean2));
}

答案 1 :(得分:13)

我建议您使用 unitils 库:

http://www.unitils.org/tutorial-reflectionassert.html

public class User {

    private long id;
    private String first;
    private String last;

    public User(long id, String first, String last) {
        this.id = id;
        this.first = first;
        this.last = last;
    }
}
User user1 = new User(1, "John", "Doe");
User user2 = new User(1, "John", "Doe");
assertReflectionEquals(user1, user2);

另见:

答案 2 :(得分:4)

您可以使用Commons LangToStringBuilder将它们转换为可读字符串,然后在两个字符串上使用assertEquals()

如果您喜欢XML,可以使用java.lang.XMLEncoder将bean转换为XML,然后比较两个XML文档。

就个人而言,我更喜欢ToStringBuilder,因为它可以让您更好地控制格式,并允许您对集合中的元素进行排序以避免误报。

我建议将bean的每个字段放在不同的行中,以便比较它们(see my blog for details)。

答案 3 :(得分:1)

我认为,最通用的方法是反映bean成员并逐个测试它们的相等性。常见的lang EqualsBuilder是一个良好的开端,它应该不是一件大事,让它(在源代码级别)适应你的要求(报告差异而不是返回等于结果)。

答案 4 :(得分:1)

对于单元测试,可以使用ReflectionEquals使用JUnit和Mockito完成。当以下面的方式实现时,它会在任何字段不相等时转储对象的JSON表示,这样可以很容易地找到有害的差异。

import static org.junit.Assert.assertThat;
import org.mockito.internal.matchers.apachecommons.ReflectionEquals;

assertThat("Validating field equivalence of objects", expectedObjectValues, new ReflectionEquals(actualObjectValues));

答案 5 :(得分:1)

您可以设置所有字段:

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.beans.HasPropertyWithValue.hasProperty;
import static org.hamcrest.Matchers.is;

@Test
public void test_returnBean(){

    arrange();

    MyBean myBean = act();

    assertThat(myBean, allOf(hasProperty("id",          is(7L)), 
                             hasProperty("name",        is("testName1")),
                             hasProperty("description", is("testDesc1"))));
}

答案 6 :(得分:0)

由于您不喜欢您引用的问题中的答案,为什么不在每个bean中使用toXml方法,将它们转换为xml文件,然后使用xmlUnit进行比较。

您可以在此处获得有关比较xml文件的更多信息:

Best way to compare 2 XML documents in Java

答案 7 :(得分:0)

你并没有真正断言平等,更多的是做“差异”。显然,“相同”的含义取决于每种类型的特定逻辑,并且差异的表示也可以变化。这个要求与传统的equals()之间的一个主要区别是,一旦看到第一个差异,通常equals()就会停止,你会希望继续并比较每个字段。

我会考虑重用一些equals()模式,但我怀疑你需要编写自己的代码。

答案 8 :(得分:0)

我要问的第一个问题是,你是否想在Bean上做'深'等于?它有孩子豆需要测试吗?您可以覆盖equals方法,但这只返回一个布尔值,因此您可以创建一个“比较器”,这可能会抛出一个异常,并显示一条不相等的消息。

在下面的示例中,我列出了几种实现equals方法的方法。

如果你想检查它们是否是同一个对象实例,那么Object的正常equals方法会告诉你。

    objectA.equals(objectB);

如果你想编写一个客户等于方法来检查对象的所有成员变量是否相等,那么你可以覆盖这样的equals方法......

  /**
     * Method to check the following...
     * <br>
     * <ul>
     *    <li>getTitle</li>
     *    <li>getInitials</li>
     *    <li>getForename</li>
     *    <li>getSurname</li>
     *    <li>getSurnamePrefix</li>
     * </ul>
     *
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj)
    {
      if (   (!compare(((ICustomer) obj).getTitle(), this.getTitle()))
          || (!compare(((ICustomer) obj).getInitials(), this.getInitials()))
          || (!compare(((ICustomer) obj).getForename(), this.getForename()))
          || (!compare(((ICustomer) obj).getSurname(), this.getSurname()))
          || (!compare(((ICustomer) obj).getSurnamePrefix(), this.getSurnamePrefix()))
          || (!compare(((ICustomer) obj).getSalutation(), this.getSalutation()))  ){
          return false;
      }
      return true;
    }

最后一个选项是使用java反射来检查equals方法中的所有成员变量。如果你真的想通过它的bean get / set方法检查每个成员变量,这很好。当两个对象的测试相同时,它不会(我不认为)允许您检查私有的memeber varibles。 (如果您的对象模型具有循环依赖性,请不要这样做,它永远不会返回)

注意:这不是我的代码,它来自......

Java Reflection equals     public static boolean equals(Object bean1,Object bean2)     {     //处理琐碎的案件     if(bean1 == bean2)     return true;

if (bean1 == null)
return false;

if (bean2 == null)
return false;

// Get the class of one of the parameters
Class clazz = bean1.getClass();

// Make sure bean1 and bean2 are the same class
if (!clazz.equals(bean2.getClass()))
{
return false;
}

// Iterate through each field looking for differences
Field[] fields = clazz.getDeclaredFields();
for (int i = 0; i < fields.length; i++)
{
// setAccessible is great (encapsulation
// purists will disagree), setting to true
// allows reflection to have access to
// private members.
fields[i].setAccessible(true);
try
{
Object value1 = fields[i].get(bean1);
Object value2 = fields[i].get(bean2);

if ((value1 == null && value2 != null) ||
(value1 != null && value2 == null))
{
return false;
}

if (value1 != null &&
value2 != null &&
!value1.equals(value2))
{
return false;
}
}
catch (IllegalArgumentException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
}

return true;

这不能告诉你区别的原因,但是当你发现一个不相等的部分时,可以通过消息发送到Log4J。

答案 9 :(得分:0)

我假设这两个bean的类型相同,在这种情况下,只有成员变量值在bean实例之间会有所不同。

定义一个名为utilAssertEquals的util类(带有私有ctor的public static final)。使用Java反射获取每个bean中每个成员变量的值。然后在不同bean中的相同成员变量的值之间执行equals()。如果相等失败,请提及字段名称。

注意:成员变量通常是私有的,因此您需要使用反射来临时更改私有成员的可访问性。

此外,根据您希望断言的细微程度,您应该考虑以下事项:

  1. 成员变量的等同性不在bean类中,而是在所有超类中。

  2. 数组中元素的相等性,以防成员变量的类型为数组。

  3. 对于跨bean的给定成员的两个值,您可以考虑使用BeanAssertEquals.assertEquals(value1,value2)而不是value1.equals(value2)。

答案 10 :(得分:0)

(以我对上述Andreas_D的评论为基础)

/** Asserts two objects are equals using a reflective equals.
 * 
 * @param message The message to display.
 * @param expected The expected result.
 * @param actual The actual result
 */
public static void assertReflectiveEquals(final String message, 
        final Object expected, final Object actual) {
    if (!EqualsBuilder.reflectionEquals(expected, actual)) {
        assertEquals(message, 
                reflectionToString(expected, ToStringStyle.SHORT_PREFIX_STYLE), 
                reflectionToString(actual, ToStringStyle.SHORT_PREFIX_STYLE));
        fail(message + "expected: <" + expected + ">  actual: <" + actual + ">");
    }
}

这是我使用的,我相信它符合所有基本要求。通过对反射ToString执行断言,Eclipse将突出显示差异。

虽然Hamcrest可以提供更好的消息,但这确实涉及到更少的代码。

答案 11 :(得分:-1)

xtendbeans library可能会引起关注:

AssertBeans.assertEqualBeans(expectedBean, actualBean);

这会产生一个JUnit ComparisonFailureàla:

expected: 
    new Person => [
        firstName = 'Homer'
        lastName = 'Simpson'
        address = new Address => [
          street = '742 Evergreen Terrace'
          city = 'SpringField'
       ]
  ]
but was:
    new Person => [
        firstName = 'Marge'
        lastName = 'Simpson'
        address = new Address => [
          street = '742 Evergreen Terrace Road'
          city = 'SpringField'
       ]
  ]

您也可以将它用于获取文本表示以用于其他目的:

String beanAsLiteralText = new XtendBeanGenerator().getExpression(yourBean)

使用这个库,你可以使用上面的语法上有效的对象初始化代码片段将它复制/粘贴到expectedBean的(Xtend)源类中,但你不必,它可以很好地完成也可以在没有Xtend的情况下使用。