Unit testing

Unit testing works on the smallest testable parts of the code that deal with few inputs and a single output. When you modify Neo4j data stores using Java code, especially in embedded applications, you make use of the ImpermanentGraphDatabase file together with Neo4j APIs to test the code. The APIs you can use include the Java API, is the Neo4j traversal framework, and Cypher. The REST API does not much use with Java code. As an alternative, you can use GraphUnit to perform the integration testing. We will take a look at both scenarios in the following sections. For the purpose of testing, let's take an example graph that initially creates two nodes and a relationship to connect them and sets properties on the nodes and the relationship. The assertions that a unit test would apply on this graph and its functionality would be:

  • The creation of nodes and relationships was as expected, and the properties and labels were set correctly
  • No additional entities were created or set, including nodes, relationships, properties, and labels
  • The already existing sections of the graph, if any, remain unaltered

Not all the preceding objectives can be fulfilled by the use of Cypher. This is because Cypher is created for declarative operations on the graph database and not for imperative ones. So, only the first criterion can be fulfilled. It is also important to note that asserting the existence of an entity in the database is simple, but the process of ensuring that no extra entities were created, or no extra labels and properties were set is a rather difficult task.

So, unit tests can be performed by asserting the graph state using the low-level native Java APIs. Let's see how we can test using the Java API and the GraphUnit framework.

Using the Java API

In order to use the testing facilities on an impermanent graph, the tests.jar file of the neo4j-kernel must be present on the classpath for use. It uses the standard JUnit fixtures to achieve the testing. This can be included in your project using the Maven dependency along with JUnit, as follows:

<dependency>
   <groupId>org.neo4j</groupId>
   <artifactId>neo4j-kernel</artifactId>
   <version>2.1.4</version>
   <scope>test</scope>
   <type>test-jar</type>
</dependency>

Note

Note that the <type>test-jar</type> inclusion is important, without this you would include neo4j-kernel but not the testing libraries.

A new database can be created for testing purposes, which will be stopped after the tests are complete. Although, it is possible to use the database being tested upon for intermediate operations, it is not advised to do this since it can cause conflicts and unnecessary writes.

@Before
public void prepareTestDatabase(){
    graphDb = new TestGraphDatabaseFactory().newImpermanentDatabase();
}

@After
public void destroyTestDatabase(){
    graphDb.shutdown();
}

In the process of testing, you create nodes and assert their existence, while enclosing the write operations in a transaction:

Node n = null;
try ( Transaction tx = graphDb.beginTx() )
{
    n = graphDb.createNode();
    n.setProperty( "title", "Developer" );
    tx.success();
}

// Check for a valid node Id
assertThat( n.getId(), is( greaterThan( -1L ) ) );

// A node is retrieved with the Id of the node created. The id's and
// property must be matching.
try ( Transaction tx = graphDb.beginTx() )
{
    Node foundNode = graphDb.getNodeById( n.getId() );
    assertThat( foundNode.getId(), is( n.getId() ) );
    assertThat( (String) foundNode.getProperty( "title" ), is( "Developer" ) );
}

GraphUnit-based unit testing

GraphUnit contains a set of assertion methods that are useful in creating Neo4j tests. What they basically do is compare whether the graph created is the same as the graph that should be created. GraphUnit addresses the unit testing problems, thus ensuring that no extra properties, nodes, or relationships were altered in the graph. It gives developers the opportunity to express the desired state of the graph using Cypher and assert that this is indeed the case. The following method is used for this purpose:

public static void assertSameGraph(GraphDatabaseService database, String sameGraphCypher)

The first parameter is the database whose state is being asserted. The second parameter is a Cypher (typically CREATE) statement that expresses the desired state of the database. The graph in the database and the graph that should be created by the Cypher statement must be identical in order for the unit test to pass. Of course, the internal Neo4j node and relationship IDs are excluded from any comparisons.

If tests on graphs are large and if it is not the developer's intention to verify the state of the entire graph, GraphUnit provides another method:

public static void assertSubgraph(GraphDatabaseService database, String subgraphCypher)

The idea is the same, except that there are additional relationships and nodes in the database that are not expressed in the Cypher statement. However, the Cypher-defined subgraph must be present in the database with exactly the same node labels, relationship types, and properties on both nodes and relationships in order for the test to pass.

Note

For more insights into the set of assertion methods, you can visit http://graphaware.com/site/framework/latest/apidocs/com/graphaware/test/unit/GraphUnit.html.

Unit testing an embedded database

We can perform a simple unit test to assert that a pairing has been successfully saved. A pairing is formed with two entities that represent nodes. Each entity has a name and a directed relation between the entities. This structure is illustrated in the following figure:

Unit testing an embedded database

The code to test this simple structure using the Java API is large and is difficult to maintain for extended time periods. GraphUnit, on the other hand, enables you to compare the current graph state to the subgraph that you intended to create, which can be represented with the help of a Cypher statement. So, all you need to do is write a Cypher statement to create a subgraph that contains the two entities along with a pairing and use it as an argument to the assertSameGraph() method:

String entitySubgraph = "create (e1:Entity {name: 'Ferrari'}), (e2:Entity {name: 'Aston Martin'}), " + "(e1)<-[:hasEntity]-(p:Pairing {affinity: 0.50, allAffinities: [0.50]}), " + "(p)-[:hasEntity]->(e2) merge (e1)-[:pairsWith]-(e2)";  
GraphUnit.assertSameGraph(getGraphDb(), entitySubgraph);

This will ensure that the structure represented by the Cypher query string is all that constitutes the graph that was created, with every property matching exactly.

GraphUnit contains another method to check whether the Neo4j graph contains a desired subgraph in it. This will assert the existence of the subgraph specified by the cypher query and filter out the other nodes and relationships:

GraphUnit.assertSubgraph(getGraphDb(), entitySubgraph);

GraphUnit needs an org.neo4j.graphdb.GraphDataService handle to the database that needs to tested. It can be used effectively with the impermanent or embedded graph databases. To test server instances of Neo4j using the REST API, we can use GraphAware RestTest, which we will discuss later in the chapter.

Apart from basic code testing, GraphUnit can also be used to verify that the import of data into a graph completes successfully or not. Since data import from CSV or other formats is not performed as transactions, this is important to check the integrity of the data load process. So, you can use GraphUnit to write a single unit test that verifies the subgraphs in the database created after the import, rather than inspecting the graph visually or running queries to check.

public class graphUnitTestDemo
{  
   @Test  
   public void testActor() 
    {  
     String actorSubgraph = "Match (country:Country {name: 'BRITAIN'})<-[:From]-(actor {name: 'PIERCE BROSNAN'}), (m:Movie {id: 5}), (min:Minutes {id: 35}) create (actor)-[:ActedIn]->(genre:Genre{type: 'Action'})-[:In]->(m) create (m)-[:BelongsTo]->(country)";  
     GraphUnit.assertSubgraph(getGraphDb(), actorSubgraph);  
    }  
   @Test  
   public void testReferee() 
    {  
     String refSubgraph = "Match (m:Movie {id:5}) match (c:Country {name:'BRITAIN'}) create (m)-[:Director]->(d:Director {name:'The Doctor'})-[:HomeCountry]->(c)";
     GraphUnit.assertSameGraph(getGraphDb(), refSubgraph);  
   }  
 }

This is a much simpler, convenient, and effective way to test the graph at hand.

There are several benefits of this testing technique, especially concerning readability. Moreover, the GraphUnit version for testing is actually more fail-safe since it results in failure, when the graph contents are more than or different to that explicitly mentioned in the Cypher statement.

Unit testing a Neo4J server

The GraphAware RestTest library was created to test code that is designed to connect to a standalone server instance of Neo4j. You can set up the testing functionality on your Neo4j server by including the following JAR files in the plugins directory:

  • graphaware-resttest-2.1.3.15.6.jar
  • graphaware-server-community-all-2.1.3.15.jar ( or graphaware-server-enterprise-all-2.1.3.15.jar, depending upon your server installation.)

Note

You can download the JAR files from http://graphaware.com/downloads/. Check the latest versions of the JAR files while downloading. You will also need to restart your server after dropping the preceding JAR files in the plugins directory to be able to use the APIs.

The testing process is pretty simple. You can direct POST requests to the predefined URLs defined in the RestTest framework to check a specific functionality. In the server mode deployment, there are three URLs defined that you can use:

  • http://server-ip-address:7474/graphaware/resttest/clear in order to clear the database.
  • http://server-ip-address:7474/graphaware/resttest/assertSameGraph in order to assert the database state. The body of the request must contain a URL-encoded Cypher CREATE statement defining the expected state of the graph.
  • http://server-ip-address:7474/graphaware/resttest/assertSubgraph in order to assert the database state for a given subgraph. The body of the request must contain a URL-encoded Cypher CREATE statement to define the state of the subgraph.

A call to the first API endpoint will clear the database. The second API endpoint provides a functionality that is similar to the assertSameGraph() method of GraphUnit. This helps verify that the graph existing in the database matches the one that will be created through the Cypher CREATE statement included in the request body. Every aspect of the graph, including nodes, relationships, their labels and properties, needs to be exactly the same. However, the internal IDs that Neo4j uses to reference nodes/relationships are ignored while matching the graphs. If the matching is successful, the response returned is an OK(200) HTTP response. Otherwise, if the assertion fails, an EXPECTATION_FAILED HTTP response is returned with a status code of 417.

In the third endpoint case, the RestTest API validates whether the graph structure created by the Cypher query provided is a subgraph of the graph in the database server. This is equivalent to the assertSubGraph() method of the GraphUnit API. The response code of the outcomes are the same as mentioned previously.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset