Monday, February 16, 2009

Bookmark and Share

DBUnit is a great tool when it comes to the management of test data. When doing data driven unit tests, database tables have to be populated with predefined records, that can be accessed by the code under test. DBUnit allows the specification of such test records by the means of data set files as in the following example:

1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <location num="6" addr="Webster Street" date="2009-02-16"/>
</dataset>

The data that can be inserted with such data set files is pretty static by nature. DBUnit offers a bit of dynamic with its ReplacementDataSet, which allows to replace custom-defined placeholders (e.g. [SYSDATE]) with other values (e.g. new Date()). But I am aware of no possibility to use more flexible expressions to insert things like "now - 14 days" into a date field for instance.

So I set out to create a data set implementation, that allows to use scripting language expressions in its fields. Using this ScriptableDataSet, data set files like the following can be loaded and processed:

1
2
3
4
<?xml version="1.0" encoding="UTF-8"?>
<dataset>
    <location num="jruby:12/2" addr="jruby:'Webster Street'.reverse" date="jruby:DateTime::now() - 14"/>
</dataset>

Having the power of a scripting language at your hands any dynamic expressions thinkable can be used in the fields of a data set – also "now - 14 days" is no problem any more.

How does it work? The scriptable data set leverages the scripting language integration API as defined by JSR 223 ("Scripting for the Java Platform"). An implementation of that API is part of Java 6, but it generally can be used with previous Java versions as well.

To use a certain language in a data set, a JSR 223 compatible engine for that language has to be added to the class path. Loads of such engines can be found at the scripting project at java.net. Having added the engine for your preferred scripting language (or by using the Rhino engine for JavaScript, which is delivered with the Java 6 JDK), a ScriptableDataSet can be created as follows:

1
2
3
4
5
6
7
8
List<Class<? extends ScriptInvocationHandler>> handlers = 
    new ArrayList<Class<? extends ScriptInvocationHandler>>();
handlers.add(TestInvocationHandler.class);

IDataSet dataSet = 
    new ScriptableDataSet(
        new FlatXmlDataSet(new File("dataset.xml")),
        new ScriptableDataSetConfig("jruby", "jruby:", handlers));

Sporting DBUnit's decorator scheme, a ScriptableDataSet is created wrapping another IDataSet. Additionally a ScriptableDataSetConfig object has to be provided, that specifies

  • the name of the scripting language to be used as understood by the JSR 223 ScriptEngineManager ("jruby")
  • a prefix that shall precede all fields containing scripts in that language ("jruby:")
  • an optional list of ScriptInvocationHandlers, that can be used to pre-process (e.g. to add common imports) and post-process scripts (e.g. to convert results into data types understood by DBUnit)

So if you like the idea of the scriptable data set, don't hesistate and get your hands on it at its git repo over at github. Do you think, it is useful at all, and if so, which scenarios for its usage could you think of?

8 comments:

Vb Reader said...

Scripting in Dataset. Excellent nice idea. But how to push back the data to database any idea

Gunnar Morling said...

Great, that you like the idea. Not sure I understand your question, though.

If you instantiate a ScriptableDataSet you can load its content into your DB using DBUnit's DatabaseOperation.INSERT.

You can find an example in scriptable dataset's unit test ScriptableDataSetTest.

Hth, Gunnar

Ben Kolera said...

This is an excellent idea. We have a lot of temporally defined data in our database (things like inventory assignments over periods and such) and also have a lot of user queries that query the data based on NOW().

This scripting allows us to create robust test data in our integration tests that are always set up relative to the current date.

Thanks for your effort into creating this code. :)

Gunnar Morling said...

Hi Ben,

great to hear that this stuff is useful for you. Just out of interest: which scripting language are you using? And do you use it for any other tasks than performing relative date inserts?

Gunnar

Anonymous said...

Love the idea. Not having any means of entering 'dynamic' data into my database was my main concern when using dbUnit.
Would love it if you could make a .jar available?

Gunnar Morling said...

To make things simple, I recommend to add scriptable-dataset as Maven dependency to your project as described in

Scriptable dataset 1.0 released

If you don't work with Maven, you also can fetch the JAR directly from the repository:

http://gunnarmorling-maven-repo.googlecode.com/svn/repo/de/gmorling/scriptable-dataset/1.0/

Hth, Gunnar

Anonymous said...

Excellent idea, just extended my test suite nicely there

Gunnar Morling said...

Great to hear, that you like the idea. What scripting language do you use?