3

I am currently using GraphUnit, Spock, and an in process Neo4j server to do integration testing on my Spring Data Neo4j 4.0 backed application.

It has been a very nifty tool in doing assertions on the state of the graph database after my test operations but I have noticed that in order for GraphUnit's assertGraph and printGraph to show me what I expect, my Neo4j transactions must be committed first. Logically this makes sense to me but it also means that I cannot mark my integration tests @Transactional and that any data changes made to the in process database in one test will bleed into subsequent tests.

I am handling this by clearing the database after every test method in a Spock fixture method and this works fine but I would very much like to be able to:

  • Set up a test Neo4j data set ONCE
  • Have test method changes roll back after each test
  • Still somehow be able to use GraphUnit's assertion and print utilities

My question is - is there a way to accomplish all three? Is it an underlying requirement / assumption that transactions commit if I want to use GraphUnit?

Luanne
  • 19,145
  • 1
  • 39
  • 51
simonl
  • 1,240
  • 7
  • 19

1 Answers1

6

While you can manage Neo4j transactions between test methods without too much difficulty, the tricky thing here is getting GraphUnit to make its assertions using the same transaction in which your test methods participate.

Given that GraphUnit requires you to work with GraphDatabaseService, your best bet is indeed to recreate your test data for each test, I'm afraid.

That said, though, you can possibly save some effort by using a reusable JUnit test rule for this, as it alleviates the need to write tear-down methods for each of your test harnesses. In a class implementing org.junit.rules.TestRule you can set up a GraphDatabaseService in its constructor and then do something like this:

@Override
public Statement apply(final Statement baseStatement, Description description) {
    setUpTestData();

    return new Statement() {
        @Override
        public void evaluate() throws Throwable {
            try {
                if (graphDatabaseService.isAvailable(1000)) {
                    baseStatement.evaluate();
                } else {
                    Assert.fail("Database was shut down or didn't become available within 1s");
                }
            } finally {
                resetDatabase();
            }
        }
    };
}

public void setUpTestData() {
    // can set up common test data here, or just use an @Before method in the test harness
}

public void resetDatabase() {
    this.graphDatabaseService.execute("MATCH (n) OPTIONAL MATCH (n)-[r]-() DELETE r, n");
}

You can include it like this:

public class ArbitraryTest {

    @Rule
    public final Neo4jTestRule neo4jRule = new Neo4jTestRule();

    @Test
    public void arbitraryTestMethod() {

        // some code...

        GraphUnit.assertSameGraph(sameGraphCypher, neo4jRule.getGraphDatabaseService());
    }

}

Note that if you include it as a static @ClassRule then it'll run the apply method only once for the entire test class, which can be more efficient but you'd have to call the reset and setup methods manually in @Before / @After methods to clean things up between test runs.

ATG
  • 1,679
  • 14
  • 25