对于最新稳定版本,请使用Spring Data Neo4j 8.0.0spring-doc.cadn.net.cn

与Spring Boot及@DataNeo4jTest

春季靴子优惠@DataNeo4jTest通过org.springframework.boot:spring-boot-starter-test. 后者带来了org.springframework.boot:spring-boot-test-autoconfigure其中包含注释和 所需的基础设施代码。spring-doc.cadn.net.cn

在Maven构建中加入Spring Boot Starter Test(春季启动测试)
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
在Gradle构建中加入Spring Boot Starter Test(春季启动测试)
dependencies {
    testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

@DataNeo4jTest是 Spring Boot 测试切片。 测试片为使用 Neo4j 的测试提供了所有必要的基础设施:事务管理器、客户端、模板以及声明仓库,无论是命令式还是响应式变体, 取决于是否存在反应性依赖。 测试切片已经包含@ExtendWith(SpringExtension.class)因此它能自动运行JUnit 5(JUnit Jupiter)。spring-doc.cadn.net.cn

@DataNeo4jTest默认提供命令式和响应式基础设施,并添加了隐式@Transactional也。@Transactional然而在 Spring 测试中,总是意味着命令式事务型,因为声明式事务需要 方法的返回类型以决定命令式是否PlatformTransactionManager或反应性ReactiveTransactionManager是必要的。spring-doc.cadn.net.cn

要为响应式仓库或服务确立正确的事务行为,你需要注入事务操作员或者用使用带注释方法的服务包裹你的领域逻辑,这些方法暴露了一个返回类型,使得 以便基础设施选择正确的事务管理器。spring-doc.cadn.net.cn

测试片不会导入嵌入式数据库或其他连接设置。 使用合适的连接取决于你自己。spring-doc.cadn.net.cn

我们建议两种选择之一:使用Neo4j Testcontainers模块或Neo4j测试线束。 虽然Testcontainers是一个已知的项目,拥有许多不同服务的模块,但Neo4j测试线束相对陌生。 它是一个嵌入式实例,在测试存储过程时尤其有用,详见《测试你的基于 Neo4j 的 Java 应用》。 不过,测试工具也可以用于测试应用程序。 由于它在与你的应用同一个JVM中调用数据库,性能和时序可能与你的生产环境不符。spring-doc.cadn.net.cn

为了方便,我们提供了三种可能的场景:Neo4j 测试线束 3.5 和 4.x/5.x,以及 Testcontainers Neo4j。 我们提供了3.5和4.x/5.x的不同示例,因为测试线束在不同版本之间有所变化。 另外,4.0需要JDK 11。spring-doc.cadn.net.cn

@DataNeo4jTest搭配Neo4j测试线束3.5

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和 适当的存储库配置。spring-doc.cadn.net.cn

使用 Neo4j 3.5 测试线束
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

Neo4j 4.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和 适当的存储库配置。spring-doc.cadn.net.cn

使用 Neo4j 4.x/5.x 测试线束
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 合作

配置连接的原则当然在测试容器中依然相同,如使用测试容器所示。 你需要以下依赖关系:spring-doc.cadn.net.cn

<dependency>
    <groupId>org.testcontainers</groupId>
    <artifactId>neo4j</artifactId>
    <version>1.17.6</version>
    <scope>test</scope>
</dependency>

还有一个完整的测试:spring-doc.cadn.net.cn

使用测试容器
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);
	}
}

替代@DynamicPropertySource

有些情况下,上述注释并不适合你的使用场景。 其中一种可能就是你想对驱动程序初始化拥有100%的控制权。 运行测试容器时,可以用嵌套的静态配置类实现这一点,比如这样:spring-doc.cadn.net.cn

@TestConfiguration(proxyBeanMethods = false)
static class TestNeo4jConfig {

    @Bean
    Driver driver() {
        return GraphDatabase.driver(
        		neo4jContainer.getBoltUrl(),
        		AuthTokens.basic("neo4j", neo4jContainer.getAdminPassword())
        );
    }
}

如果你想使用属性但不能使用@DynamicPropertySource你会使用初始化器:spring-doc.cadn.net.cn

动态性质的替代注入
@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());
        }
    }
}