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
|
@NodeEntitypublic 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
|
@NodeEntitypublic 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 .