Spring Data Neo4j — это проект в рамках проекта Spring Data, который предоставляет расширение модели программирования Spring для написания приложений, использующих Neo4j в качестве графической базы данных. Для написания тестов с использованием NoSQLUnit для приложений Spring Data Neo4j вам не нужно ничего особенного, кроме того, что Spring Data Neo4j использует специальное свойство type
в узлах и связях графа, в котором хранится полное имя класса этой сущности.
Помимо свойства type
на уровне узла / отношения, нам также необходимо создать один индекс для узлов и один индекс для отношений. В случае узлов rel_types
имя индекса types
, а для отношений требуется rel_types
. В обоих случаях мы должны установить значение ключа в className
и значение в полное имя класса.
Тип сопоставления
IndexingNodeTypeRepresentationStrategy
и IndexingRelationshipTypeRepresentationStrategy
используются в качестве реализации сопоставления типов по умолчанию, но вы также можете использовать SubReferenceNodeTypeRepresentationStrategy
которая сохраняет типы сущностей в дереве в графе, представляющем иерархию типов и интерфейсов, или вы можете настроить еще больше, реализовав интерфейс интерфейса NodeTypeRepresentationStrategy
.
Руки на работе
заявка
Звездный флот попросил нас разработать приложение для хранения всех членов звёздного флота , их отношений с другими членами звёздного флота и корабля, на котором они служат. Лучший способ реализовать это требование — использовать базу данных Neo4j в качестве бэкэнд-системы. Кроме того, Spring Data Neo4j используется на постоянном уровне. Это приложение смоделировано на два класса Java , один для членов и другой для звездолетов. Обратите внимание, что для этого примера в отношениях нет свойств, поэтому моделируются только узлы.
Член класса
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
|
@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 } |
Звездный класс
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
@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 } |
Помимо классов моделей нам также нужны два репозитория для реализации операций CRUD и xml
файл xml
context . Spring Data Neo4j использует инфраструктуру Spring Data Commons, позволяющую нам создавать основанные на интерфейсе композиции репозиториев, обеспечивая реализации по умолчанию для определенных операций.
Класс MemberRepository
1
2
3
4
5
6
|
public interface MemberRepository extends GraphRepository<Member>, RelationshipOperationsRepository<Member> { Member findByName(String name); } |
Обратите внимание, что помимо операций, предоставляемых интерфейсом GrapRepository
таких как save
, findById
, findById
, … мы определяем один метод запроса, который также называется findByName
. Репозитории Spring Data Neo4j (и большинство проектов Spring Data ) предоставляют механизм для определения запросов с использованием известного подхода Ruby on Rails для определения запросов поиска.
StarshipRepository class
1
2
3
|
public interface StarshipRepository extends GraphRepository<Starship>, RelationshipOperationsRepository<Starship> { } |
файл контекста приложения
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
xsi:schemaLocation="http://www.springframework.org/schema/beans < context:component-scan base-package = "com.lordofthejars.nosqlunit.springdata.neo4j" /> < context:annotation-config /> < neo4j:repositories base-package = "com.lordofthejars.nosqlunit.springdata.repository" /> </ beans > |
тестирование
Модульное тестирование
Как было сказано ранее, для написания наборов данных для Spring Data Neo4j нам не нужно делать ничего особенного, кроме правильного создания свойств узла и отношений и определения необходимых индексов. Давайте посмотрим набор данных, используемый для тестирования метода findByName
путем findByName
База данных Neo4j .
файл star-trek-TNG-dataset.xml
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
<? xml version = "1.0" ?> 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 > |
Обратите внимание, что каждый узел имеет по крайней мере одно свойство type
с полным квалифицированным именем класса и индексом с types
имен, ключом className
и полным квалифицированным именем класса в качестве значения. Следующий шаг — настройка контекста приложения для модульных тестов. приложения контекстно-погруженный neo4j.xml
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
xsi:schemaLocation="http://www.springframework.org/schema/beans < import resource = "classpath:com/lordofthejars/nosqlunit/springdata/neo4j/application-context.xml" /> < neo4j:config storeDirectory = "target/config-test" /> </ beans > |
Обратите внимание, что мы используем пространство имен Neo4j для создания экземпляров встроенной базы данных Neo4j . И теперь мы можем написать тестовый пример JUnit : WhenInformationAboutAMemberIsRequired
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
@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); } } |
В предыдущем уроке есть несколько важных моментов, на которые следует обратить внимание:
- Напомним, что нам нужно использовать объект Spring
ApplicationContext
для извлечения встроенного экземпляра Neo4j, определенного в контексте приложения Spring . - Поскольку жизненный цикл базы данных управляется контейнером Spring Data , нет необходимости определять какой-либо менеджер жизненного цикла NoSQLUnit .
Интеграционный тест
Модульные тесты обычно выполняются на встроенных экземплярах в памяти, но в производственной среде вам может потребоваться доступ к внешним серверам Neo4j с помощью соединения Rest или в случае создания экземпляра SpringRestGraphDatabase
класса Spring Data Neo4j . Вам нужно написать тесты для проверки того, что ваше приложение все еще работает, когда вы интегрируете свой код с удаленным сервером, и эти тесты обычно называют интеграционными тестами. SpringRestGraphDatabase
интеграционных тестов для нашего приложения так же просто, как определить контекст приложения с помощью SpringRestGraphDatabase
и позволить NoSQLUnit управлять жизненным циклом базы данных Neo4j .
1
|
.application-context-managed-neo4j.xml |
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
xsi:schemaLocation="http://www.springframework.org/schema/beans < import resource = "classpath:com/lordofthejars/nosqlunit/springdata/neo4j/application-context.xml" /> < bean id = "graphDatabaseService" class = "org.springframework.data.neo4j.rest.SpringRestGraphDatabase" > </ bean > < neo4j:config graphDatabaseService = "graphDatabaseService" /> </ beans > |
Обратите внимание, что вместо регистрации внедренного экземпляра мы настраиваем класс SpringRestGraphDatabase
для подключения к серверу localhost. И давайте реализуем интеграционный тест, который проверяет, что все звездные корабли могут быть получены с сервера Neo4j .
WhenInformationAboutAMemberIsRequired
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
@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); } } |
Поскольку метод defaultSpringGraphDatabaseServiceNeo4j
возвращает экземпляр GraphDatabaseService
определенный в контексте приложения , в нашем случае он возвращает определенный экземпляр SpringRestGraphDatabase
.
Выводы
Между написанием тестов для приложений Spring Data Neo4j нет особой разницы между теми, которые они используют. Только помните, чтобы правильно определить свойство type
и создать необходимые индексы. Также обратите внимание, что с точки зрения NoSQLUnit нет разницы между написанием модульных или интеграционных тестов, кроме управления жизненным циклом ядра базы данных. Скачать код
Ссылка: тестирование приложений Spring Data Neo4j с NoSQLUnit от нашего партнера по JCG Алекса Сото в блоге One Jar To Rule All .