Thursday, December 06, 2007

Unit testing Persistent objects in ORMs such as NHibernate

Even though I am writing this with NHibernate-specific examples, these concepts apply to any ORM technology, so use your imagination a little on this one.

So you're using persistent classes, and you need to make unit tests. Here is a fundamental concern to test with any persistent class:

I want to create an object, then "persist" it. I want to test that the object loaded from persistence matches the object I originally created. Then I have tested the correctness of the mappings.

How do you test to make sure that two objects match each other? I see two concerns:
  1. The immediate values inside of the object are matching (primitive properties, for example)
  2. The "neighbors" that my class has references to also match (many-to-one or one-to-many associated classes).
Consider this scenario: You have a target class X, and you want to test its persistence. Imagine that X inherits from an abstract class such as PersistentObject:
    public abstract class PersistentObject<T>
{
#region members
private int id = 0;
#endregion

#region properties
public int Id
{
get { return id; }
}
public bool IsSaved
{
get { return id != 0; }
}
#endregion

#region methods
public abstract bool Matches(T t);
#endregion
}
This is a very simplistic example, but in this case every PersistentObject has an integer identity primary key. Anything that inherits from PersistentObject must also implement a "Matches" method, which compares objects of a common type to see if the properties match.
I could have overridden the object.Equals method here, but I feel this would obfuscate the meaning of "Equals", so I made a new method.

This "Matches" method is the perfect hook to test the equality of immediate properties (persistent object testing concern #1).

Just because the immediate properties of an object match, does this mean that the objects are the same? No, we must make sure that the "neighboring" objects also match.

For concern #2, we could use a recursive approach, call the "Matches" method recursively on all of the parents and all of the children? I argue this is overkill.

If the target object's parents' IDs are matching, and the target object's childrens' IDs collections are matching, then we have effectively proven that the objects match.

If you have a test for every type of persistent object, then there is no need to call a "Matches" method recursively, because you will be testing the same thing many times over, and the code will be unnecessarily complex.

One important quality of NHibernate to remember: NHibernate guaranteess the reference equality between two objects representing the same row under the same session. This is a very simple, but powerful quality.

Here is the approach I've adopted for unit testing persistence:
  1. Create your database schema from the mappings using the schema export tool.
  2. Use a factory to create some bulk data. If you can, create a rich graph of all of your persistent objects in a realistic scenario.
  3. Using a new session, persist this object graph to the database.
  4. For each type of persistent object,
  • try to load a new copy of the object under a brand new session using the ID of one of your originally created objects.
  • Call the "Matches" method to compare the two objects. (concern #1)
  • Verify that the IDs of the parents/children of the original object equal the IDs of the parents/children of the loaded objects (concern #2)
Finally, tear down the database at the end.

Labels: ,

0 Comments:

Post a Comment

<< Home