Spring boot integration test fails with Neo4j

2019-06-06 10:09发布

问题:

I am new to spring boot. I use Spring Boot 1.5.1 GA and Neo4j starter of spring boot. I tried to create my very first integration test to whether I can insert a new object into the graph database. Here is my test class:

package hu.bookandwalk;
import static org.junit.Assert.assertEquals;
import java.time.LocalDateTime;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.neo4j.ogm.testutil.TestServer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import hu.bookandwalk.domain.Address;
import hu.bookandwalk.domain.InvoiceData;
import hu.bookandwalk.domain.User;
import hu.bookandwalk.repository.UserRepository;
import java.util.HashSet;
import java.util.Set;

@ContextConfiguration(classes = {PersistenceTestContext.class})
@RunWith(SpringRunner.class)
@SpringBootTest
public class BnWEbookstoreApplicationTests {

    private static TestServer testServer;

    @Autowired
    UserRepository userRepository;

    @BeforeClass
    public static void setupTestServer() {
        /* just a port without any intention */
        testServer = new TestServer.Builder().build();
    }

    @AfterClass
    public static void stopTestServer() {
       testServer.shutdown();
    }

    @Test
    public void ShouldInsertUser() {
        Address a=new Address("Hungary","Budapest","1092","Erkel street 9",LocalDateTime.now());
        InvoiceData invoiceData=new InvoiceData("BandW", "233456", true, a, LocalDateTime.now());

        Set<InvoiceData> invoiceDatas=new HashSet<InvoiceData>();
        invoiceDatas.add(invoiceData);
        Set<Address> addresses=null;

        User u=new User(1l,"Roland","email@gmail.com","pwd",true,LocalDateTime.now(), true,invoiceDatas,addresses);
        userRepository.save(u);
        assertEquals(1l, userRepository.count());
    }

The PersistentTestContext is used specifically to the test of database operations. In this class I set the embedded driver for testing:

package hu.bookandwalk;

import org.neo4j.ogm.drivers.embedded.driver.EmbeddedDriver;
import org.neo4j.ogm.session.SessionFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.repository.config.EnableNeo4jRepositories;
import org.springframework.data.neo4j.transaction.Neo4jTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@EnableNeo4jRepositories("org.neo4j.cineasts.repository")
@EnableTransactionManagement
@ComponentScan("hu.bookandwalk")
public class PersistenceTestContext {
    @Bean
    public org.neo4j.ogm.config.Configuration configuration() {
        final org.neo4j.ogm.config.Configuration configuration = new org.neo4j.ogm.config.Configuration();
        configuration.driverConfiguration().setDriverClassName(EmbeddedDriver.class.getName());
        return configuration;
    }
@Bean
public SessionFactory sessionFactory() {
    return new SessionFactory(configuration(), "hu.bookandwalk.domain");
}

@Bean
public PlatformTransactionManager transactionManager() {
    return new Neo4jTransactionManager(sessionFactory());
}

}

Unfortunately, when I run my test from my STS IDE, I got the following error message before even the test can be validated:

java.lang.NoClassDefFoundError: org/eclipse/jetty/server/SessionManager
    at org.neo4j.server.CommunityNeoServer.createWebServer(CommunityNeoServer.java:89)
    at org.neo4j.server.AbstractNeoServer.init(AbstractNeoServer.java:181)
    at org.neo4j.server.AbstractNeoServer.start(AbstractNeoServer.java:196)
    at org.neo4j.harness.internal.InProcessServerControls.start(InProcessServerControls.java:72)
    at org.neo4j.harness.internal.AbstractInProcessServerBuilder.newServer(AbstractInProcessServerBuilder.java:137)
    at org.neo4j.ogm.testutil.TestServer.startServer(TestServer.java:73)
    at org.neo4j.ogm.testutil.TestServer.<init>(TestServer.java:55)
    at org.neo4j.ogm.testutil.TestServer.<init>(TestServer.java:37)
    at org.neo4j.ogm.testutil.TestServer$Builder.build(TestServer.java:239)
    at hu.bookandwalk.BnWEbookstoreApplicationTests.setupTestServer(BnWEbookstoreApplicationTests.java:36)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: java.lang.ClassNotFoundException: org.eclipse.jetty.server.SessionManager
    at java.net.URLClassLoader.findClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
    at java.lang.ClassLoader.loadClass(Unknown Source)
    ... 29 more

java.lang.NullPointerException
    at hu.bookandwalk.BnWEbookstoreApplicationTests.stopTestServer(BnWEbookstoreApplicationTests.java:41)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
    at java.lang.reflect.Method.invoke(Unknown Source)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:33)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86)
    at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382)
    at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)

When I created my project I used the Spring Starter project template. As far as I remember I did not put any other dependencies into my pom.xml in addition to the ones put by Spring Boot. I only add some properties like for thymeleaf to use version 3 instead of 2. You can see my pom.xml here:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.example</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>BnWEbookstore</name>
<description>Book and Walk e-book webáruház</description>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.5.1.RELEASE</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <thymeleaf.version>3.0.3.RELEASE</thymeleaf.version>
    <thymeleaf-layout-dialect.version>2.1.2</thymeleaf-layout-dialect.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-cache</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-neo4j</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-rest</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-rest-hal-browser</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-hateoas</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mail</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-mobile</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-security</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-social-facebook</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-social-twitter</artifactId>
    </dependency>
    <dependency>
        <groupId>org.thymeleaf</groupId>
        <artifactId>thymeleaf-spring4</artifactId>
    </dependency>
    <dependency>
        <groupId>org.thymeleaf.extras</groupId>
        <artifactId>thymeleaf-extras-java8time</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.neo4j</groupId>
        <artifactId>neo4j-ogm-test</artifactId>
        <version>2.1.1</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

<repositories>
    <repository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>https://repo.spring.io/snapshot</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </repository>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>
<pluginRepositories>
    <pluginRepository>
        <id>spring-snapshots</id>
        <name>Spring Snapshots</name>
        <url>https://repo.spring.io/snapshot</url>
        <snapshots>
            <enabled>true</enabled>
        </snapshots>
    </pluginRepository>
    <pluginRepository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </pluginRepository>
</pluginRepositories>

When I check the transitive dependencies I see that neo4j-ogm-test indirectly pull the jetty sever 9.4.1. Any idea what can cause this problem?

回答1:

Seems like you're missing a dependency on the embedded driver. If you add the following to your POM does it work?

    <dependency>
        <groupId>org.neo4j</groupId>
        <artifactId>neo4j-ogm-embedded-driver</artifactId>
        <version>${neo4j-ogm.version}</version>
        <scope>test</scope>
    </dependency>


回答2:

A shortcut to get a valid setup is to install neo4j locally and check the /lib directory to find a good match between the version of neo4j and jetty-server

I got the same error running the "vanilla" installation of spring-data-neo4j. According to the default installation of neo4j I could spot the following compatible versions:

  • neo4j-* version 3.1.2
  • org.eclipse.jetty.* version 2.9.2 - spring insists me to use 2.3.*

Spring-boot wants me to run jetty-* 2.3.* which the SessionManager was removed by some reason.



回答3:

Spring Boot's default version of Jetty is newer and incompatible with the version required by Neo4j's embedded Jetty server. You can workaround the issue by forcing the version of Jetty used by Spring Boot. To do this with Gradle you specify the following in build.gradle:

ext['jetty.version'] = '9.2.9.v20150224'

For Maven I imagine it would be:

<jetty.version>9.2.9.v20150224</jetty.version>

Note that 9.2.9.v20150224 should be replaced with whatever transitive version of Jetty is required by the version of Neo4j you are using.