ModeShape

An open-source, federated content repository

Speedy unit testing with Jackrabbit

We’re using Apache Jackrabbit for one of the JCR implementations in our unit tests. Configuring Jackrabbit isn’t intuitive at first (like many libraries, it’s highly configurable and thus non-trivial to configure), so the trick for us was figuring out how we wanted to use it in our unit tests.

One of the more important qualities of a unit test is that its fast. We do a lot of unit testing, and so we run unit tests very frequently. Change, compile, run tests. Repeat. Repeat again, and again. So the slower the tests take to run, the more they interrupt this process and your train of thought. (More on our testing philosophy and techniques in a future post.)

So we’ve found that the easiest way to speed up Jackrabbit is to use the in-memory persistence manager and the in-memory file system implementations. Here’s a snippet of the XML configuration showing the in-memory file system for the “/repository” branch:

<filesystem class="org.apache.jackrabbit.core.fs.mem.MemoryFileSystem">
  <param name="path" value="${rep.home}/repository"/>
</filesystem>

and here’s a snippet showing the XML configuration for the in-memory persistence manager:

<PersistenceManager class="org.apache.jackrabbit.core.persistence.mem.InMemPersistenceManager">
  <param name="persistent" value="false"/>
</PersistenceManager>

Remember, there are two persistence managers and three file system managers in the normal configuration, so make sure to change all of them.

Then in your test code, create an instance of the TransientRepository class by passing in the location of your configuration file and the location of the directory used for the repository data. We’re using Maven 2, so our configuration file goes in “./src/test/resources/” while we use “./target/testdata/jackrabbittest/repository” for the test data directory.

We’re also using JUnit (version 4.4), so one decision we had to make was whether to set up the repository in a @Before method and tear it down in an @After method. This makes all the tests easy to write, but it also means that the repository is set up and torn down for every test case. That means slower than necessary. And since I like to have a single test class for each class, my test cases often have a mixture of test methods that need a repository and test methods that don’t.

The pattern we’ve settled on is to create an abstract base class that sets up the repository in a “startRepository()” method, and in the @After method automatically tear it down if needed. That means in our unit test case classes that use Jackrabbit, simply extend the base class, and call “startRepository()” in those test methods that need the repository. Test methods that don’t need a repository don’t take the time to set it up. Plus, I personally like that this explicit call makes it more obvious which test needs the repository.

There’s one final twist. The TransientRepository cleans itself up when the last session is closed (not when the instance is garbage collected). Since some tests try saving saving changes in a session, closing the session and opening a new one can make all this data go away. To fix this, our “startRepository()” method creates a “keep alive” session, and our @After tear down method closes the session if it’s there.

Here’s the basics of our abstract base class:

private static Repository repository;private Session keepAliveSession;
    @BeforeClass
    public static void beforeAll() throws Exception {
        // Clean up the test data ...
        FileUtil.delete(JACKRABBIT_DIRECTORY_PATH);

       // Set up the transient repository (this shouldn't do anything yet)...
       repository = new TransientRepository(REPOSITORY_CONFIG_PATH,REPOSITORY_DIRECTORY_PATH);
}

@AfterClass
public static void afterAll() throws Exception {
    try {
        JackrabbitRepository jackrabbit = (JackrabbitRepository)repository;
        jackrabbit.shutdown();
    } finally {
        // Clean up the test data ...
        FileUtil.delete(JACKRABBIT_DATA_PATH);
    }
}

public void startRepository() throws Exception {
    if (keepAliveSession == null) {
        keepAliveSession = repository.login();
    }
}

@After
public void shutdownRepository() throws Exception {
    if (keepAliveSession != null) {
        try {
            keepAliveSession.logout();
        } finally {
            keepAliveSession = null;
        }
    }
}

So setting up unit tests is a piece of cake, and they run very quickly. Now we’re getting somewhere.

About these ads

Filed under: techniques, testing, tools

2 Responses

  1. Pie says:

    Thank you sooo much, I was honestly thinking that suicide might have been preferable to figuring out how to make a totally in memory repository! Do you know where the use of the params such as “persistent” is documented?

    Thanks

    • Randall says:

      Unfortunately, I don’t know offhand where that information is documented. It’s been a long time since we’ve used Jackrabbit in our builds, since ModeShape is another JCR implementation.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

ModeShape is

a lightweight, fast, pluggable, open-source JCR repository that federates and unifies content from multiple systems, including files systems, databases, data grids, other repositories, etc.

Use the JCR API to access the information you already have, or use it like a conventional JCR system (just with more ways to persist your content).

ModeShape used to be 'JBoss DNA'. It's the same project, same community, same license, and same software.

ModeShape

Topics

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: