Test Driven Development and Neo4J: Using @Rule and Avoiding Containers
Join the DZone community and get the full member experience.
Join For FreeBeen doing some coding with Neo4J. Pretty interesting experience so far, but one thing is for sure: being able to write seriously sophisticated code that has a large, complex model underneath it, from inside unit tests that run in a few milliseconds is, as the bloodsucking credit card commercial says, priceless. There are still a couple of challenges to consider, but the reality is with a simple custom rule (using JUnit 4), you can have an embedded database that will be ready for each test, and cleaned up after each one.
Here is the code. Because of the inability to put a @Rule inside a rule implementation, the folder is passed in. Notice the shutdown is commented out. Per my prior post, that is because the folder is already gone by the time this gets called. Turns out it‘s not really needed. If you manage the resource yourself, however, you have to call shutdown or your deletes will not wipe the dir completely.
package com.ontometrics.testing; import org.junit.rules.ExternalResource; import org.junit.rules.TemporaryFolder; import org.neo4j.graphdb.Transaction; import org.neo4j.kernel.EmbeddedGraphDatabase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Custom @Rule implementation for making an embeddable graph database for use * in tests. * <p> * Does the setup and teardown of the database, creates a transaction. * * see EmbeddedGraphDatabase, {</code>link ExternalResource} */ public class TestGraphDatabase extends ExternalResource { private static final Logger log = LoggerFactory.getLogger(TestGraphDatabase.class); public TemporaryFolder tempFolder; private EmbeddedGraphDatabase database; private Transaction transaction; public TestGraphDatabase(TemporaryFolder tempFolder) { this.tempFolder = tempFolder; } @Override protected void before() throws Throwable { super.before(); } @Override protected void after() { super.after(); transaction.finish(); // database.shutdown(); } public EmbeddedGraphDatabase getDatabase() { if (database == null) { log.debug("created database in: {}", tempFolder.getRoot().getAbsolutePath()); database = new EmbeddedGraphDatabase(tempFolder.getRoot().getAbsolutePath()); transaction = database.beginTx(); } return database; } public Transaction getTransaction() { return transaction; } }
Yeah, the formatting of code on here is abysmally bad.
Also,
I wait until the database is asked for because getting the temp folder
in Before was not working. You could do it there instead since we are
passing the folder in.
I am burnt to a crisp on container-based testing in Java. My new meanderings are that if there is a need for a container, it‘s probably not until long into the development cycle. Which brought up a bunch of interesting questions:
- Doesn‘t the Rule in JUnit attempt to solve the same problem that the container does? When we write our own classes for things like EntityManagers, we quickly get dispirited about the question of how we are going to make these things magically materialize in all the places that we need them. But is that really such a big problem?? For instance, I wrote a few tests that used this rule and put objects in the db, created relations, retrieved them, etc. Then I wanted to make a repository for my entity. So I did a generic repository that uses Reflection (did this in Spring, something like it in Seam). Only, this time, I didn‘t jump right away into the Container Fry Pan. So the Repository needs an EM, big deal. Let‘s start by making one and setting it. Then I got to thinking, ‘why not make a test class (not in compile scope) that just makes your repositories for you, sticks the EM inside, then returns it? Or, why not embed a Rule for each major repository in that package‘s test equivalent? So PersonRepository extends ExternalResource.. ? Actually, PersonRepository extends TestRepository and that class could get the EMF wiring?
- Is there really a requirement to start calling the container into service from the very beginning? Frankly, what if we went totally naked on the domain side then had a controller layer that had some generic injection protocol (our own annotations) so we could develop all the way up through that layer with no container and then could turn on CDI or its ilk once we were up on that floor?
- Isn‘t there a container equivalent of the very anemia Spring was supposedly made to cure?? Yes there is friends. We‘ve all seen the DI projects where the same simplistic patterns of DAOs are repeated robotically to an inane degree.
- If we move away from a per-domain object/single persistence entry point, doesn‘t the container become less important? The Weld maven archetype had this idea and some of their docs too. So, instead of making a PersonRepository that ends up with 80 methods in it, you have controllers injected with the EM and then different functional requirements of the Person are associated with their context more directly. So, for instance, if we have a social bent to our app, we might have a FriendsController that manages invites and the like, and if we have security another called PersonalSecurityController that has those interactions. To me, this is less anemic, less prone to bloat, and less monocultural. It‘s also plenty easy to do without seeing the container as the master puppeteer who has to bring all the forces together.
This was what I was getting at in my post about Play. The container guys have both over played their hand and lost site of the real costs of their approach, and by staying reactionary, have failed to redirect their attention forward, and half their audience is too young to even remember the supposed glory of their birth. Furthermore, as Agile continues to turn more and more into Lean, we will see that the decade of the DI container has been anything but.
From http://www.jroller.com/robwilliams/entry/tdd_and_neo4j_rule_no
Opinions expressed by DZone contributors are their own.
Comments