Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Persisting complex test data

We are using builder pattern to generate test data. These domain objects have relations between them. Our functional tests require these objects to be persisted.

Think about this model:

domain model

If I want a plain instance of C I do aNew().c().build()

If I want it to be persisted I do aNew().c().saveIn(session)

If I want an instance of C with a known B I do aNew().c().with(b).build()

Well, you got the idea. My problem is, if I want to persist a C, should it persist it's B? Or should it be persisted before hand? What about if I want a reasonable default B? What about if I want to persist a D? Should it persist all A, B, C?

Of course real system is much more complex (sometimes with circular references). I am looking for a best practice for persisting complex test data.

Edit: It looks like I have bumped into the language barrier, my mother language is not English, so I am sorry for obscurity. Here is more information:

  • It is not legacy code that I am trying to test
  • I am trying to write a coverage test, NOT a unit test (as a result I won't be mocking anything)
  • The piece of software I am trying to test works if the database is populated to some extend (it does not use all entities).

PS. Please don't hesitate to ask for more information, because I have been struggling to find the possible best practice. The closest thing I have come up with is:

  1. Keep track of what has been set explicitly while building an entity.
  2. Assume that explicitly set entities are already persisted, do not persist them.
  3. Persist everything else (with their own persister).

This will work, but my spider sense is tingling, I think I am doing something wrong because, there will be logic involved in test code, it will be very complex to deal with without tests.

Edit 2: I will try to make myself more clear. When I am writing/running my unit and some integration tests I have no problem, because the test data are not persisted, it lives in memory.

But when I try to persist my test data, hibernate will not let me save an entity without it's relations.

How can I overcome this problem?

like image 310
nimcap Avatar asked Sep 24 '09 12:09

nimcap


3 Answers

You should probably describe your test setup in more detail. In particular, why do your functional tests require these objects to be persisted? Are you testing the actual persistence operation? Or is that just a side effect of running the tests? Do you want to load persisted objects as part of your tests?

My problem is, if I want to persist a C, should it persist it's B? Or should it be persisted before hand?

This will depend on why you are persisting in the first place. If you are integration testing the persistence layer, then you should just use the logic the application itself uses. If it's just a side effect of testing, you might want to mock the persistence layer, etc...

like image 100
sleske Avatar answered Oct 05 '22 08:10

sleske


You need to define your cascades on the domain better. If you can't test it, how do you expect it will perform in the real application?

For example:

A -> B: Who's the owner of this relationship? Do you want to add B to A, or the other way around? This can be an implementation detail where you can have both B.SetParent(A) and A.Children.Add(B), and where you set B's parent to A in case of A.Children.Add(B) (likewise the other way around). What happens if you do:

A a1 = new A();
A a2 = new A();
B b = new B();
a1.Children.Add(b);
b.SetParent(a);

You need to make up your mind here. None of the solutions are perfect, so it's basically personal preference and app consistency that applies here.

Working with ORMs you get into these constraint problems faster then with plain SQL (or any other datasource like XML or your own datasource), but you'd need to consider the problems if you were to write plain SQL too.

I'm sorry, I don't have an definite answer for you, but to me it looks like you need to consider some constraints which (I presume) you haven't done yet.

Personally, I like the repository-pattern when dealing using NHibernate in DALs. I make my repositories implement from IDisposable and let them get a session each. This way you get the "Unit of work"-pattern into your design.

Good luck with it :)

like image 32
cwap Avatar answered Oct 05 '22 09:10

cwap


I separated your answers by topic.

My problem is, if I want to persist a C, should it persist it's B? What about if I want to persist a D? Should it persist all A, B, C?

This is entirely dependent upon the domain constraints you choose to enforce. For example, is C an entity and B a value object? In other words, does C have a unique identity and life of its own? Is B mainly identified by its value and its life cycle tightly coupled to that of its parent C?

Asking these types of questions should help guide your decisions on what to persist, when, and by whom.

For example, if both C and B are entities sharing only a relationship, you might decide to persist them independantly, since each could conceivably have a meaningful life and identity of its own. If B is a value object, you'd probably choose to have its parent entity C control its life, including the creation/retrieval/updating/deleting of the object. This might very well include C persisting B.

Or should it be persisted before hand?

To answer this you could have to map out your object dependencies. These dependencies are frequently represented by foreign key constraints when an object graph is persisted to a RDBMS. If C could not function without a reference to B, then you would probably want to persist them both inside a transaction, with B being done first to comply with the database's foreign key constraints. Following the line of thought above, if B was a child entity or value object of C, you might even have C responsible for persisting B.

What about if I want a reasonable default B?

The creation of B instances could be delegated to the B-Factory. Whether you implement this factory logic as a class (not instance) method, constructor, or separate it out as its own unit doesn't matter. The point is you have one place where the creation and configuration of new Bs takes place. It is in this place that you would have a default configuration of the newly instantiated object take place.

An excellent resource covering these types of questions is Domain-Driven Design by Eric Evans

like image 38
Robert Campbell Avatar answered Oct 05 '22 07:10

Robert Campbell