此版本仍在开发中,尚未被视为稳定版本。对于最新的稳定版本,请使用 Spring Data Neo4j 7.4.4! |
使用 Spring Boot 和@DataNeo4jTest
Spring Boot 提供@DataNeo4jTest
通过org.springframework.boot:spring-boot-starter-test
.
后者带来了org.springframework.boot:spring-boot-test-autoconfigure
其中包含注解和
所需的基础设施代码。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
@DataNeo4jTest
是 Spring Boot 测试片。
测试切片为使用 Neo4j 的测试提供了所有必要的基础设施:事务管理器、客户端、模板和声明的存储库,它们的命令式或反应式变体,
取决于是否存在反应式依赖项。
测试切片已包含@ExtendWith(SpringExtension.class)
,以便它与 JUnit 5 (JUnit Jupiter) 一起自动运行。
@DataNeo4jTest
默认提供命令式和响应式基础设施,并且还添加了一个隐式@Transactional
也。@Transactional
然而,在 Spring 测试中,总是意味着命令式事务,因为声明式事务需要
return 类型来确定命令式PlatformTransactionManager
或反应式ReactiveTransactionManager
是必需的。
要为响应式仓库或服务断言正确的事务行为,您需要注入TransactionalOperator
到测试中,或者将域逻辑包装在服务中,这些服务使用带注释的方法公开返回类型,从而使之成为可能
为基础结构选择正确的事务管理器。
测试切片不会引入嵌入式数据库或任何其他连接设置。 使用适当的连接取决于您。
我们建议使用以下两个选项之一:使用 Neo4j Testcontainers 模块或 Neo4j 测试工具。 虽然 Testcontainers 是一个已知的项目,其中包含许多不同服务的模块,但 Neo4j 测试工具却相当不为人知。 它是一个嵌入式实例,在测试存储过程时特别有用,如测试基于 Neo4j 的 Java 应用程序中所述。 但是,测试工具也可用于测试应用程序。 由于它在与应用程序相同的 JVM 中启动数据库,因此性能和时间可能与您的生产设置不同。
为方便起见,我们提供了三种可能的方案:Neo4j 自动化测试框架 3.5 和 4.x/5.x 以及 Testcontainers Neo4j。 我们为 3.5 和 4.x/5.x 提供了不同的示例,因为这些版本之间的测试框架发生了变化。 此外,4.0 需要 JDK 11。
@DataNeo4jTest
使用 Neo4j 测试工具 3.5
您需要以下依赖项才能运行 Using Neo4j 3.5 测试框架:
<dependency>
<groupId>org.neo4j.test</groupId>
<artifactId>neo4j-harness</artifactId>
<version>3.5.33</version>
<scope>test</scope>
</dependency>
企业版 Neo4j 3.5 的依赖项位于com.neo4j.test:neo4j-harness-enterprise
和
适当的存储库配置。
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Optional;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.neo4j.harness.ServerControls;
import org.neo4j.harness.TestServerBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
@DataNeo4jTest
class MovieRepositoryTest {
private static ServerControls embeddedDatabaseServer;
@BeforeAll
static void initializeNeo4j() {
embeddedDatabaseServer = TestServerBuilders.newInProcessBuilder() (1)
.newServer();
}
@AfterAll
static void stopNeo4j() {
embeddedDatabaseServer.close(); (2)
}
@DynamicPropertySource (3)
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", embeddedDatabaseServer::boltURI);
registry.add("spring.neo4j.authentication.username", () -> "neo4j");
registry.add("spring.neo4j.authentication.password", () -> null);
}
@Test
public void findSomethingShouldWork(@Autowired Neo4jClient client) {
Optional<Long> result = client.query("MATCH (n) RETURN COUNT(n)")
.fetchAs(Long.class)
.one();
assertThat(result).hasValue(0L);
}
}
1
Entrypoint to create an embedded Neo4j
2
This is a Spring Boot annotation that allows for dynamically registered
application properties. We overwrite the corresponding Neo4j settings.
3
Shutdown Neo4j after all tests.
@DataNeo4jTest
with Neo4j test harness 4.x/5.x
You need the following dependencies to run Using Neo4j 4.x/5.x test harness:
Neo4j 4.x test harness dependencies
<dependency>
<groupId>org.neo4j.test</groupId>
<artifactId>neo4j-harness</artifactId>
<version>4.4.25</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
</exclusion>
</exclusions>
</dependency>
The dependencies for the enterprise version of Neo4j 4.x/5.x are available under the com.neo4j.test:neo4j-harness-enterprise
and
an appropriate repository configuration.
Using Neo4j 4.x/5.x test harness
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Optional;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.neo4j.harness.Neo4j;
import org.neo4j.harness.Neo4jBuilders;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
@DataNeo4jTest
class MovieRepositoryTest {
private static Neo4j embeddedDatabaseServer;
@BeforeAll
static void initializeNeo4j() {
embeddedDatabaseServer = Neo4jBuilders.newInProcessBuilder() (1)
.withDisabledServer() (2)
.build();
}
@DynamicPropertySource (3)
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", embeddedDatabaseServer::boltURI);
registry.add("spring.neo4j.authentication.username", () -> "neo4j");
registry.add("spring.neo4j.authentication.password", () -> null);
}
@AfterAll
static void stopNeo4j() {
embeddedDatabaseServer.close(); (4)
}
@Test
public void findSomethingShouldWork(@Autowired Neo4jClient client) {
Optional<Long> result = client.query("MATCH (n) RETURN COUNT(n)")
.fetchAs(Long.class)
.one();
assertThat(result).hasValue(0L);
}
}
1
Entrypoint to create an embedded Neo4j
2
Disable the unneeded Neo4j HTTP server
3
This is a Spring Boot annotation that allows for dynamically registered
application properties. We overwrite the corresponding Neo4j settings.
4
Shut down Neo4j after all tests.
@DataNeo4jTest
with Testcontainers Neo4j
The principal of configuring the connection is of course still the same with Testcontainers as shown in Using Test containers.
You need the following dependencies:
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>neo4j</artifactId>
<version>1.17.6</version>
<scope>test</scope>
</dependency>
And a complete test:
Using Test containers
import static org.assertj.core.api.Assertions.assertThat;
import java.util.Optional;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.data.neo4j.DataNeo4jTest;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.test.context.DynamicPropertyRegistry;
import org.springframework.test.context.DynamicPropertySource;
import org.testcontainers.containers.Neo4jContainer;
@DataNeo4jTest
class MovieRepositoryTCTest {
private static Neo4jContainer<?> neo4jContainer;
@BeforeAll
static void initializeNeo4j() {
neo4jContainer = new Neo4jContainer<>()
.withAdminPassword("somePassword");
neo4jContainer.start();
}
@AfterAll
static void stopNeo4j() {
neo4jContainer.close();
}
@DynamicPropertySource
static void neo4jProperties(DynamicPropertyRegistry registry) {
registry.add("spring.neo4j.uri", neo4jContainer::getBoltUrl);
registry.add("spring.neo4j.authentication.username", () -> "neo4j");
registry.add("spring.neo4j.authentication.password", neo4jContainer::getAdminPassword);
}
@Test
public void findSomethingShouldWork(@Autowired Neo4jClient client) {
Optional<Long> result = client.query("MATCH (n) RETURN COUNT(n)")
.fetchAs(Long.class)
.one();
assertThat(result).hasValue(0L);
}
}
Alternatives to a @DynamicPropertySource
There are some scenarios in which the above annotation does not fit your use case.
One of those might be that you want to have 100% control over how the driver is initialized.
With a test container running, you could do this with a nested, static configuration class like this:
@TestConfiguration(proxyBeanMethods = false)
static class TestNeo4jConfig {
@Bean
Driver driver() {
return GraphDatabase.driver(
neo4jContainer.getBoltUrl(),
AuthTokens.basic("neo4j", neo4jContainer.getAdminPassword())
);
}
}
If you want to use the properties but cannot use a @DynamicPropertySource
, you would use an initializer:
Alternative injection of dynamic properties
@ContextConfiguration(initializers = PriorToBoot226Test.Initializer.class)
@DataNeo4jTest
class PriorToBoot226Test {
private static Neo4jContainer<?> neo4jContainer;
@BeforeAll
static void initializeNeo4j() {
neo4jContainer = new Neo4jContainer<>()
.withAdminPassword("somePassword");
neo4jContainer.start();
}
@AfterAll
static void stopNeo4j() {
neo4jContainer.close();
}
static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
TestPropertyValues.of(
"spring.neo4j.uri=" + neo4jContainer.getBoltUrl(),
"spring.neo4j.authentication.username=neo4j",
"spring.neo4j.authentication.password=" + neo4jContainer.getAdminPassword()
).applyTo(configurableApplicationContext.getEnvironment());
}
}
}