Mike Caputo
Mike Caputo

Reputation: 1156

Proper way to compare object in a Unit test

I'm writing unit tests for my grails application, and I realized I don't really know the proper way to assert whether an object is the proper object or not.

For example, given this test:

void testExampleTest() {
    mockSession.person = new Person(firstName:'John', lastName:'Doe', middleInitial:'E')
    def model = controller.testMethod()
    ...assertions...
}

and

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

how should I make sure that the person object I added to the session is properly being passed in the model? Is it sufficient to use

assertEquals( person,model['person'] )

or because I injected the object myself into the session does it make more sense to use

assertEquals( person.firstName, model['person'].firstName )
assertEquals( person.lastName, model['person'].lastName )
assertequals( person.middleName, model['person'].middleName )

It seems to me that the first way should suffice as long as the object has a properly defined equals method, but I just wanted to see what the conventional way is.

Thanks

Upvotes: 6

Views: 8649

Answers (8)

Victor Sergienko
Victor Sergienko

Reputation: 13475

Property-by-property comparison needs to be repeated in every test - so it's a good old code duplication, a test smell described in XUnitPatterns. Better have a proper equals().

Of course, you can add an utility method personEquals() or even override Person.equals() in runtime. For mocked class, you will probably have to. I personally stick to shorter code which is just one assertEquals() when possible.

Upvotes: 3

Bob The Janitor
Bob The Janitor

Reputation: 20802

I have found that doing property by property is a little more reliable and gives you a little bit more fine grain control over how something is compared, the down side is it's a little more work to set up and maintain

Upvotes: 1

Ryan Stewart
Ryan Stewart

Reputation: 128859

In this particular instance, testing the individual properties is only a way for you to identify a specific instance of an object, and it clouds the meaning of the test. What you specifically care about and should assert is that model['person'] is the exact same object as what you initially put in as person:

assertSame(person, model['person'])

Or with Hamcrest, which allows much more expressive assertions overall:

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

Upvotes: 1

Burt Beckwith
Burt Beckwith

Reputation: 75671

I use assertSame(). Comparing field by field is way more work than necessary - you mocked the data, so just assert that the mocked values are properly returned.

Upvotes: 0

Ryan Gross
Ryan Gross

Reputation: 6515

In Grails every object is serializable, so you could compare the two using their XML Serializations:

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);
}

If you're working in eclipse, you'll get a great textual comparison of the two XML strings showing all of the differences.

Upvotes: 0

Christian Semrau
Christian Semrau

Reputation: 9013

As you wrote, if the test data has a proper equals method, you can use it. "Proper" here means that it tests the attributes you want to be tested.

I often work with database entities which only compare their ID attribute. With these objects, I need to test each attribute separately to see if they are equal. I wrote a little helper that allows me to write a single assert for many properties, like this:

assertEqualProperties(person, model['person'], "firstName", "lastName", "middleName");

This helper method uses reflection to access the attributes (not directly, I invoke the commons-beans library). In Groovy, there surely is a syntax that does not need explicit reflection. The method reports the first non-equal attribute as a test failure.

Upvotes: 0

Daff
Daff

Reputation: 44215

If equals is defined properly you are right. The problem is, that you might have to first unit test if equals is defined properly (meaning it behaves the way you expect it to).

This might get a little more difficult if you create a mockup for the Person class. In that case you don't care if equals works properly because you only want to check if some attributes are being set/accessed properly. This is why I prefer checking for primitive values if possible and necessary. I find that it makes the tests also more descriptive (although it can become pretty verbose).

Upvotes: 1

djna
djna

Reputation: 55907

Funny, I and a colleague had a similar discussion today. Our conclusion was that

An advantage of the more laborious attribute-by-attribute comparison is that it reports a specific difference rather than just a "no, they are not equals", and this may be convenient.

Also we did not have control over certain classes, and some of those lacked an equals method.

We intend to investigate whether it's possible to use reflection to implement a comparator, hence removing some of the tedium.

Upvotes: 1

Related Questions