Testing Spring Data Neo4j Applications with NoSQLUnit
Join the DZone community and get the full member experience.
Join For Free
spring data neo4j
spring data neo4j
is the project within
spring data
project which provides an extension to the
spring
programming model for writing applications that uses
neo4j
as graph database.
to write tests using
nosqlunit
for
spring data neo4j
applications, you do need nothing special apart from considering that
spring data neo4j
uses a special property called
member class
starship class
memberrepository class
starshiprepository class
application-context file
star-trek-tng-dataset.xml file
application-context-embedded-neo4j.xml
wheninformationaboutamemberisrequired
wheninformationaboutamemberisrequired
download code
we keep learning,
alex.
type
in graph nodes and relationships which stores the fully qualified classname of that entity.
apart from
type
property at node/relationship
level, we also need to create one index for nodes and one index for
relationships. in case of nodes,
types
index name is required, meanwhile
rel_types
is required for relationships. in both cases we must set key value to
classname
and value to full qualified classname.
|
type mapping
indexingnodetyperepresentationstrategy
and
indexingrelationshiptyperepresentationstrategy
are used as default type mapping implementation, but you can also use
subreferencenodetyperepresentationstrategy
which stores entity types in a tree in the graph representing the type
and interface hierarchy, or you can customize even more by implementing
nodetyperepresentationstrategy
interface.
|
hands on work
application
starfleet
has asked us to develop an application for storing
all starfleet members, with their relationship with other starfleet
members, and the ship where they serve.
the best way to implement this requirement is using
neo4j
database as backend system. moreover
spring data neo4j
is used at persistence layer.
this application is modelized into two
java
classes, one for
members and another one for starships. note that for this example there
are no properties in relationships, so only nodes are modelized.
member class
@nodeentity public class member { private static final string commands = "commands"; @graphid long nodeid; private string name; private starship assignedstarship; public member() { super(); } public member(string name) { this.name = name; } @fetch @relatedto(type=commands, direction=direction.outgoing) private set<member> commands; public void command(member member) { this.commands.add(member); } public set<member> commands() { return this.commands; } public starship getassignedstarship() { return assignedstarship; } public string getname() { return name; } public void assignedin(starship starship) { this.assignedstarship = starship; } //equals and hash methods }
@nodeentity public class member { private static final string commands = "commands"; @graphid long nodeid; private string name; private starship assignedstarship; public member() { super(); } public member(string name) { this.name = name; } @fetch @relatedto(type=commands, direction=direction.outgoing) private set<member> commands; public void command(member member) { this.commands.add(member); } public set<member> commands() { return this.commands; } public starship getassignedstarship() { return assignedstarship; } public string getname() { return name; } public void assignedin(starship starship) { this.assignedstarship = starship; } //equals and hash methods } |
@nodeentity public class starship { private static final string assigned = "assignedstarship"; @graphid long nodeid; private string starship; public starship() { super(); } public starship(string starship) { this.starship = starship; } @relatedto(type = assigned, direction=direction.incoming) private set<member> crew; public string getstarship() { return starship; } public void setstarship(string starship) { this.starship = starship; } //equals and hash methods }
apart from model classes, we also need two repositories for implementing crud operations, and
spring context
xml
file.
spring data neo4j
uses
spring data commons
infrastructure allowing us to create interface based compositions of
repositories, providing default implementations for certain operations.
memberrepository class
public interface memberrepository extends graphrepository<member>, relationshipoperationsrepository<member> { member findbyname(string name); }
see that apart from operations provided by
graprepository
interface like
save
,
findall
,
findbyid
, … we are defining one query method too called
findbyname
.
spring data neo4j
repositories (and most of
spring data
projects) provide a mechanism to define queries using the known
ruby on rails
approach for defining finder queries.
starshiprepository class
public interface starshiprepository extends graphrepository<starship>, relationshipoperationsrepository<starship> { }
application-context file
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:neo4j="http://www.springframework.org/schema/data/neo4j" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd"> <context:component-scan base-package="com.lordofthejars.nosqlunit.springdata.neo4j"/> <context:annotation-config/> <neo4j:repositories base-package="com.lordofthejars.nosqlunit.springdata.repository"/> </beans>
testing
unit testing
as it has been told previously, for writing datasets for
spring data neo4j
,
we don’t have to do anything special beyond creating node and
relationship properties correctly and defining the required indexes.
let’s see the dataset used to test the
findbyname
method by seeding
neo4j
database.
star-trek-tng-dataset.xml file
<?xml version="1.0" ?> <graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xsi:schemalocation="http://graphml.graphdrawing.org/xmlns http://graphml.graphdrawing.org/xmlns/1.0/graphml.xsd"> <key id="name" for="node" attr.name="name" attr.type="string"></key> <key id="__type__" for="node" attr.name="__type__" attr.type="string"></key> <key id="starship" for="node" attr.name="starship" attr.type="string"></key> <graph id="g" edgedefault="directed"> <node id="3"> <data key="__type__">com.lordofthejars.nosqlunit.springdata.neo4j.member</data> <data key="name">jean-luc picard</data> <index name="__types__" key="classname">com.lordofthejars.nosqlunit.springdata.neo4j.member</index> </node> <node id="1"> <data key="__type__">com.lordofthejars.nosqlunit.springdata.neo4j.member</data> <data key="name">william riker</data> <index name="__types__" key="classname">com.lordofthejars.nosqlunit.springdata.neo4j.member</index> </node> <node id="4"> <data key="__type__">com.lordofthejars.nosqlunit.springdata.neo4j.starship</data> <data key="starship">ncc-1701-e</data> <index name="__types__" key="classname">com.lordofthejars.nosqlunit.springdata.neo4j.starship</index> </node> <edge id="11" source="3" target="4" label="assignedstarship"></edge> <edge id="12" source="1" target="4" label="assignedstarship"></edge> <edge id="13" source="3" target="1" label="commands"></edge> </graph> </graphml>
see that each node has at least one
type
property with full qualified classname and an index with name
types
, key
classname
and full qualified classname as value.
next step is configuring application context for unit tests.
application-context-embedded-neo4j.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:neo4j="http://www.springframework.org/schema/data/neo4j" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd"> <import resource="classpath:com/lordofthejars/nosqlunit/springdata/neo4j/application-context.xml"/> <neo4j:config storedirectory="target/config-test"/> </beans>
notice that we are using
neo4j
namespace for instantiating an embedded
neo4j
database.
and now we can write the
junit
test case:
wheninformationaboutamemberisrequired
@runwith(springjunit4classrunner.class) @contextconfiguration("application-context-embedded-neo4j.xml") public class wheninformationaboutamemberisrequired { @autowired private memberrepository memberrepository; @autowired private starshiprepository starshiprepository; @autowired private applicationcontext applicationcontext; @rule public neo4jrule neo4jrule = newneo4jrule() .defaultspringgraphdatabaseserviceneo4j(); @test @usingdataset(locations = "star-trek-tng-dataset.xml", loadstrategy = loadstrategyenum.clean_insert) public void information_about_starship_where_serves_and_members_under_his_service_should_be_retrieved() { member jeanluc = memberrepository.findbyname("jean-luc picard"); assertthat(jeanluc, is(createmember("jean-luc picard"))); assertthat(jeanluc.commands(), containsinanyorder(createmember("william riker"))); starship starship = starshiprepository.findone(jeanluc.getassignedstarship().nodeid); assertthat(starship, is(createstarship("ncc-1701-e"))); } private object createstarship(string starship) { return new starship(starship); } private static member createmember(string membername) { return new member(membername); } }
there are some important points in the previous class to take under consideration:
-
recall that we need to use spring
applicationcontext
object to retrieve embedded neo4j instance defined into spring application context. -
since lifecycle of database is managed by spring data container, there is no need to define any nosqlunit lifecycle manager.
integration test
unit tests are usually run against embedded in-memory instances, but in
production environment you may require access to external
neo4j
servers by using
rest
connection, or in case of
spring data neo4j
instantiating
springrestgraphdatabase
class. you need to write tests to validate that your application still
works when you integrate your code with a remote server, and this tests
are typically known as integration tests.
to write integration tests for our application is as easy as defining an application context with
springrestgraphdatabase
and allow
nosqlunit
to control the lifecycle of
neo4j
database.
.application-context-managed-neo4j.xml
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xmlschema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:neo4j="http://www.springframework.org/schema/data/neo4j" xsi:schemalocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/data/neo4j http://www.springframework.org/schema/data/neo4j/spring-neo4j.xsd"> <import resource="classpath:com/lordofthejars/nosqlunit/springdata/neo4j/application-context.xml"/> <bean id="graphdatabaseservice" class="org.springframework.data.neo4j.rest.springrestgraphdatabase"> <constructor-arg index="0" value="http://localhost:7474/db/data"></constructor-arg> </bean> <neo4j:config graphdatabaseservice="graphdatabaseservice"/> </beans>
note how instead of registering an embedded instance, we are configuring
springrestgraphdatabase
class to connect to localhost server. and let’s implement an
integration test which verifies that all starships can be retrieved from
neo4j
server.
wheninformationaboutamemberisrequired
@runwith(springjunit4classrunner.class) @contextconfiguration("application-context-managed-neo4j.xml") public class wheninformationaboutstarshipsarerequired { @classrule public static managedneoserver managedneoserver = newmanagedneo4jserverrule() .neo4jpath( "/users/alexsotobueno/applications/neo4j-community-1.7.2") .build(); @autowired private starshiprepository starshiprepository; @autowired private applicationcontext applicationcontext; @rule public neo4jrule neo4jrule = newneo4jrule() .defaultspringgraphdatabaseserviceneo4j(); @test @usingdataset(locations = "star-trek-tng-dataset.xml", loadstrategy = loadstrategyenum.clean_insert) public void information_about_starship_where_serves_and_members_under_his_service_should_be_retrieved() { endresult<starship> allstarship = starshiprepository.findall(); assertthat(allstarship, containsinanyorder(createstarship("ncc-1701-e"))); } private object createstarship(string starship) { return new starship(starship); } }
because
defaultspringgraphdatabaseserviceneo4j
method returns a
graphdatabaseservice
instance defined into
application context
, in our case it will return the defined
springrestgraphdatabase
instance.
conclusions
there is not much difference between writing tests for none
spring data neo4j
applications than the ones they use it. only keep in mind to define correctly the
type
property and create required indexes.
also see that from the point of view of
nosqlunit
there is no difference between writing unit or integration tests, apart of lifecycle management of the database engine.
download code
we keep learning,
alex.
Spring Data
Neo4j
application
Spring Framework
Data (computing)
Database
unit test
Published at DZone with permission of Alex Soto, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
Designing a New Framework for Ephemeral Resources
-
Building the World's Most Resilient To-Do List Application With Node.js, K8s, and Distributed SQL
-
RAML vs. OAS: Which Is the Best API Specification for Your Project?
-
Send Email Using Spring Boot (SMTP Integration)
Comments