与Spring Boot及@DataNeo4jTest
春季靴子优惠@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 测试中,始终意味着命令式事务,因为声明式事务需要方法的返回类型来决定命令式是否PlatformTransactionManager或反应性ReactiveTransactionManager是必要的。
要为响应式仓库或服务确立正确的事务行为,你需要注入事务操作员进入测试,或者将你的域逻辑包裹在使用注释方法的服务中,这些方法暴露了一个返回类型,使得基础设施能够选择正确的事务管理器。
测试片不会导入嵌入式数据库或其他连接设置。使用合适的连接取决于你自己。
我们推荐两种选择之一:要么使用 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 | 创建嵌入式 Neo4j 的入口 |
| 2 | 这是一个 Spring Boot 注释,允许动态注册应用属性。我们覆盖了相应的 Neo4j 设置。 |
| 3 | 所有测试后关闭Neo4j。 |
@DataNeo4jTest配合Neo4j测试线束4.x/5.x
运行 Using Neo4j 4.x/5.x 测试线带需要以下依赖:
<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>
企业版 Neo4j 4.x/5.x 的依赖可在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.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 | 创建嵌入式 Neo4j 的入口 |
| 2 | 禁用不必要的 Neo4j HTTP 服务器 |
| 3 | 这是一个 Spring Boot 注释,允许动态注册应用属性。我们覆盖了相应的 Neo4j 设置。 |
| 4 | 所有测试后关闭Neo4j。 |
@DataNeo4jTest与Testcontainers Neo4j 合作
配置连接的原则当然与Testcontainers相同,如“使用测试容器”所示。你需要以下依赖关系:
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-neo4j</artifactId>
<version>${testcontainers.version}</version>
<scope>test</scope>
</dependency>
还有一个完整的测试:
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.neo4j.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);
}
}
替代@DynamicPropertySource
有些情况下,上述注释并不适合你的使用场景。其中一种情况可能是你希望对驱动初始化拥有100%的控制权。在测试容器运行时,你可以用嵌套的静态配置类实现这一点,比如这样:
@TestConfiguration(proxyBeanMethods = false)
static class TestNeo4jConfig {
@Bean
Driver driver() {
return GraphDatabase.driver(
neo4jContainer.getBoltUrl(),
AuthTokens.basic("neo4j", neo4jContainer.getAdminPassword())
);
}
}
如果你想使用属性但不能使用@DynamicPropertySource你会使用初始化器:
@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());
}
}
}