Skip to end of metadata
Go to start of metadata

The Synthetic Database (SDB) component is a Dentaku service that provides a source for dynamically generated entities of a deterministic nature. It is primarily useful in environments where the cost to deploy and maintain sample databases for unit tests is outweighed by the need to provide change resilience to the database modelers. In conjunction with the Gentaku build-time entity generators, the SDB is able to dynamically return entities that appear to the client to be returned from a persistent store. Relations are fully supported, including many-to-one and one-to-many relationships. Primitive and basic object attributes of entities are filled with values that can be easily calculated for returned object validation.

Note: As of this writing, the SDB is "read-mostly", meaning that the data that is read will always conform to the semantic determinism that is documented herein. Changes are gracefully ignored. Attempts to persist entities are accepted without failure, but are not persisted such that they may be subsequently retrieved. Deletions from the persistent store are also accepted, but entities are not actually removed. This functionality is expected shortly. As the changes that are required to provide this functionality are small, this document is written with the expected behavior of these changes included.

History and Rationale

The primary focus of SDB is unit testing of code that relies on a persistent store. Developers often find the process of writing code to be testable and efficient at runtime to be a bit obtuse. As the state of the art changes, patterns such as Inversion of Control (IoC) will become more prevalent, but will not remove the need to provide mock persistence to clients that need to interact with the persistent store. Dentaku, while internally IoC-focused, does not export that requirement to clients. Providing alternate persistence for unit testing is very difficult when the persistence manager is not an injected dependency. Since Dentaku already provides persistence as a service, several problems were solved at once by simply providing the SDB as a component that is pluggable into the Dentaku framework rather than simply as a generic injected dependency.

Software development is a rather abstract process for those not skilled in the art, and as a result, a static business data model is generally the exception rather than the rule. And as enterprise software systems are generally funded by people with an eye on the clock and their wallet, projects are generally lacking specification, and in turn, scheduled unrealistically. Methodologies such as eXtreme Programming successfully embrace this mayhem to a large degree (and do so better than most other methodologies), but only from a process perspective and not from a practical perspective.

Dentaku, and SDB, need to support and add value to whatever methodology and level of organization that it is applied to. So as we can hope that every organization will have a stable data model well before the first line of code is written, we realize that this is almost never true and instead are focused on how to most effectively manage this change through the development lifecycle. And in the case of the SDB, the focus was on the question of change to the database model itself.

The data model generally forms the foundation of an application, and changes to the model generally create very significant consequences for the rest of the application. When these consequences affect the actual code, the changes are made very sparingly and are considered to be "a cost of doing business". Grudgingly, changes to the model are propagated out to the rest of the code, the build repaired, and hopefully with solid unit tests, validated very quickly ... except when it isn't. Changes to the model often require additional changes to the tests themselves, and if persistence is injected as a dependency rather than provided as a service of the framework, additional changes must be made to the hand-generated persistent store that is used for unit testing, or in the case of a live database being used as a backing store, to that database. What started out as a simple change to the model has suddenly turned into a major undertaking, but changes to the model such as these are commonplace.

While the actual changes to code are not less numerous in the Dentaku environment, it became clear that Gentaku could be used to almost completely manage the changes required for the injected test persistence, and do so without mechanical errors that are a part of handwritten code. As common business model changes generally require similar parts effort for the update of test model, test case, and actual code, it's not unreasonable to expect to realize this savings directly as Dentaku removes all effort from model maintenance. While this provides great benefits to organizations that make every effort to freeze their data model early, it acts as an enabler to organizations that are less willing or able to put this kind of effort into initial planning.

Usage

Using the SDB requires deployment considerations as well as semantic considerations to be addressed. Deployment considerations are very simple, it's just a matter of substituting the SDB component in place of whatever persistent store you might otherwise want to work with. When the entities are generated with Gentaku, the SDB can return synthetic entities that have compatible semantics to what was defined in the UML source. An example container snippet follows. This was taken from the JUnit test base class in the 'example-web' project, which you should definitely examine and/or steal for your own project:

This simply sets the SyntheticPersistenceManager as the PersistenceManager implementation that will be returned by a call to a lookup on the container. As SyntheticPersistenceManager is based on AbstractPersistenceManager, a set of factories for the entities are required to be set. These factories are set up automatically by the container when the model artifacts (including the generated Plexus configuration file) are on the classpath.

The semantics of using the SDB are a bit more complex, but not by much:

  • All operations provided by the PersistenceManager interface are supported by SDB.
  • Queries to SDB always return all entities that exist for the requested type.
  • Many-to-one relations from an object to another object always return one object (per definition of the relation), and relations from that returned object (if any) will in turn also return only one object.
  • One-to-many relations return a List (downcast to a collection as appropriate). Relations (if any) from the first element of the List (index zero) will contain collection relations that contain no relations. Relations (if any) from index one of the List will contain collection relations that contain only a one relation. Relations from all subsequent relations will contain all of the objects for a related type (the same as what is returned for a query on that type).

The size of a collection is defined by class.hashCode() modulo 100, plus two for the zero and one index entries. This variability is useful for testing, but is deterministic so that the number of expected results can be compared. The number of entities for a given type can be found via the static method getTestCollectionSize().

A point of note is that the test collection methods are not a part of the ModelEntity interface. This is because the test collection methods are only temporarily located in the entities themselves – this temporary collocation is a violation of separation between application and test code, and it's likely that a superclass of the entity will be generated as well, with that superclass containing the test methods. We'll see, but what we have works fine until a volunteer steps up to take on this very simple task.

Results in Action

The result is that string fields of the entities are filled with the name of the field concatenated with the toString() of the primary key. Numeric fields are filled with the primary key itself if a number is used as the key. The rules by which the fields are filled are kept consistent so that the unit test can examine the results (very often a DTO that was created by copying from fields in the entity) and know where the data came from. From the testing perspective, having data such as "John Smith" for a name is pleasing to the eye, but a list of names is not easy to automatically validate for correct data. On the other hand, "name75" does not look like it might in production and may not be great for a demo to stakeholders, but if a unit test is looking at record number 75, it is very easy to validate that this is the correct data for the "name" field.

Making changes to the database is a very easy as well. There are no additional requirements during modeling, and changes to the model are reflected directly into the entities that are returned by the SDB.

For example code using the SDB, please see the code that has been generated in the example-web project!

What's Next?

SDB is a new concept, and if you find that it doesn't meet your needs for unit testing, please be vocal about it. Your comments are appreciated and we promise to respond to every email request. If you have a question about usage, the best place to ask those are at user@dentaku.codehaus.org. If you are confident of your usage and simply want that feature implemented, please enter a feature request at http://jira.codehaus.org in the Dentaku project.

Labels
  • None