在单元测试中比较对象的正确方法

6
我正在为我的Grails应用编写单元测试,并意识到我不知道正确的断言方式来判断一个对象是否是正确的对象。
例如,给定以下测试:
void testExampleTest() {
    mockSession.person = new Person(firstName:'John', lastName:'Doe', middleInitial:'E')
    def model = controller.testMethod()
    ...assertions...
}

并且

def testMethod = {
    Person currPerson = session.getAttribute("person")
    render(view:'view',model:[person:currPerson]
}

我该如何确保我添加到会话中的person对象在模型中正确传递?仅使用以下代码是否足够:
assertEquals( person,model['person'] )

还是因为我自己将对象注入到会话中,所以使用更合理?
assertEquals( person.firstName, model['person'].firstName )
assertEquals( person.lastName, model['person'].lastName )
assertequals( person.middleName, model['person'].middleName )

我认为只要对象有一个正确定义的equals方法,第一种方式就足够了,但我只是想看看传统的方式是什么。
谢谢。
8个回答

4
每次测试都需要逐个属性进行比较,这样就会产生代码重复,也就是XUnitPatterns中描述的测试坏味道。最好使用适当的equals()方法。
当然,你可以添加一个实用方法personEquals(),甚至在运行时覆盖Person.equals()方法。对于模拟类,您可能需要这样做。我个人喜欢尽可能使用更短的代码,只需一个assertEquals()方法即可。

2
有趣的是,我和一位同事今天进行了类似的讨论。我们的结论是,逐个属性比较更费力,但它报告的是具体的差异,而不仅仅是“不相等”,这可能很方便。
此外,我们无法控制某些类,其中一些缺少equals方法。
我们打算调查是否可以使用反射来实现比较器,从而减少一些繁琐的工作。

请参见我的回答:commons-beans库使得使用基于反射的访问变得简单,可以用来构建多属性断言方法。 - Christian Semrau

2

我发现逐个属性进行比较更加可靠,而且可以对比较方式进行更细致的控制。不过缺点是设置和维护工作会更多一些。


1
在这种情况下,测试各个属性只是为了识别对象的特定实例,它会混淆测试的含义。你真正关心并应该断言的是,model['person']与最初放入person的确切对象相同:
assertSame(person, model['person'])

或者使用 Hamcrest,它可以提供更加表达力强的断言:

assertThat(model['person'], sameInstance(person))

实际上,如果被测试的对象预期产生与输入相同的实例,则应该确切地测试这一点。 - Christian Semrau

1

如果 equals 被正确定义了,你是对的。问题在于,你可能需要先进行单元测试以确定 equals 是否被正确定义(也就是它是否按照你的预期行为)。

如果你为 Person 类创建一个模拟对象,这可能会变得更加困难。在这种情况下,你不关心 equals 是否正常工作,因为你只想检查某些属性是否被正确设置/访问。这就是为什么我更喜欢尽可能和必要时检查原始值。我发现这样做也使测试更具描述性(尽管可能会变得相当冗长)。


0
如你所写,如果测试数据有适当的equals方法,则可以使用它。这里的“适当”意味着它测试了你想要测试的属性。
我经常与只比较其ID属性的数据库实体一起工作。对于这些对象,我需要分别测试每个属性,以查看它们是否相等。我编写了一个小助手,使我能够为许多属性编写单个断言,像这样:
assertEqualProperties(person, model['person'], "firstName", "lastName", "middleName");

这个帮助方法使用反射访问属性(不是直接访问,我调用了commons-beans库)。在Groovy中,肯定有一种语法不需要显式反射。该方法将第一个非相等属性报告为测试失败。


0
在Grails中,每个对象都是可序列化的,因此您可以使用它们的XML序列化来比较这两个对象:
public void compareXML(Object a, Object b)
    ByteArrayOutputStream aBaos = new ByteArrayOutputStream();
    XMLEncoder aEncoder = new XMLEncoder(aBaos);
    aEncoder.writeObject(a);
    aEncoder.close();
    String xmlA = baos.toString();

    ByteArrayOutputStream bBaos = new ByteArrayOutputStream();
    XMLEncoder bEncoder = new XMLEncoder(bBaos);
    bEncoder.writeObject(b);
    bEncoder.close();
    String xmlB = bBaos.toString();

    assertEquals(xmlA, xmlB);
}

如果您正在使用Eclipse,您将获得两个XML字符串的出色文本比较,显示所有差异。

Grails的GUnit在文本表示对象差异方面做得更好。 - Victor Sergienko

0

我使用 assertSame()。逐个比较字段的工作量大得多,完全没有必要-因为你已经模拟数据了,所以只需断言正确返回模拟值即可。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接