使用Unitils对Postgres数据库运行单元测试时出现NullPointerException

时间:2017-05-14 21:14:01

标签: java spring postgresql junit unitils

我有下表(迁移):

databaseChangeLog:
  - changeSet:
      id: 1
      author: me
      changes:
        - createTable:
            tableName: person
            columns:
              - column:
                  name: id
                  type: int
                  autoIncrement: true
                  constraints:
                    primaryKey: true
                    nullable: false
              - column:
                  name: first_name
                  type: varchar(255)
                  constraints:
                    nullable: false
              - column:
                  name: last_name
                  type: varchar(255)
                  constraints:
                    nullable: false
  - changeSet:
      id: 2
      author: me
      changes:
        - insert:
            tableName: person
            columns:
              - column:
                  name: first_name
                  value: First
              - column:
                  name: last_name
                  value: Last

以下DAO:

public interface HelloDao {
    @SqlQuery(
            "SELECT * FROM person"
    )
    @Mapper(PersonMapper.class)
    Person getPerson();
}

使用以下实体+ mapper:

public class Person {
    private String firstName;
    private String lastName;

    public Person(String firstName, String lastName) {
        this.firstName = firstName;
        this.lastName = lastName;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
}

-

public class PersonMapper implements ResultSetMapper<Person> {
    public Person map(int index, ResultSet r, StatementContext ctx) throws SQLException {
        return new Person(r.getString("first_name"), r.getString("last_name"));
    }
}

我使用junit和unitils为这个Dao编写单元测试。我有unitils.properties文件设置如下:

database.driverClassName=org.postgresql.Driver
database.url=jdbc:postgresql://localhost:5432/springbootdb
database.dialect=postgresql
database.userName=<username>
database.password=<password>
database.schemaNames=public

为了清楚起见,这是我的application.properties文件:

spring.thymeleaf.cache=false
spring.main.show-banner=false
logging.level.jdbc=OFF
logging.level.jdbc.sqltiming=DEBUG
logging.level.jdbc.resultsettable=DEBUG

spring.datasource.url= jdbc:postgresql://localhost:5432/springbootdb
spring.datasource.username=<username> 
spring.datasource.password=<password>
spring.jpa.hibernate.ddl-auto=create-drop

这是我的测试:

@Transactional(TransactionMode.ROLLBACK)
@RunWith(UnitilsBlockJUnit4ClassRunner.class)
public class HelloDaoTest {
    @TestDataSource
    DataSource dataSource;

    private DBI dbi;
    private HelloDao helloDao;

    @Before
    public void setUp() throws Exception {
        dbi = new DBI(dataSource);
        helloDao = dbi.onDemand(HelloDao.class);
    }

    @Test
    @DataSet("HelloDaoTest.testSet.xml")
    public void helloDao() throws Exception {
        Person p = helloDao.getPerson();
        assertEquals("First", p.getFirstName());
    }

}

我的夹具(HelloDaoTest.testSet.xml

<?xml version="1.0" encoding="UTF-8" ?>
    <dataset>
        <person
            first_name="First"
            last_name="Last"
        />
    </dataset>

和pom.xml文件:

<?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.experiment.springboot</groupId>
    <artifactId>testspringbootproj</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <name>Spring Boot Blank Project (from https://github.com/making/spring-boot-blank)</name>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.2.7.RELEASE</version>
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <start-class>com.experiment.springboot.App</start-class>
        <java.version>1.8</java.version>
        <lombok.version>1.14.8</lombok.version>
        <log4jdbc.log4j2.version>1.16</log4jdbc.log4j2.version>
        <rest.assured.version>2.3.3</rest.assured.version>
        <jdbi.version>2.78</jdbi.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>com.jayway.restassured</groupId>
            <artifactId>rest-assured</artifactId>
            <version>${rest.assured.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.bgee.log4jdbc-log4j2</groupId>
            <artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
            <version>${log4jdbc.log4j2.version}</version>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>9.4-1206-jdbc42</version>
        </dependency>
        <dependency>
            <groupId>org.jdbi</groupId>
            <artifactId>jdbi</artifactId>
            <version>${jdbi.version}</version>
        </dependency>
        <dependency>
            <groupId>org.liquibase</groupId>
            <artifactId>liquibase-core</artifactId>
        </dependency>
        <dependency>
            <groupId>org.liquibase</groupId>
            <artifactId>liquibase-maven-plugin</artifactId>
            <version>3.4.1</version>
        </dependency>
        <dependency>
            <groupId>org.unitils</groupId>
            <artifactId>unitils-dbunit</artifactId>
            <version>3.4.6</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <dependencies>
                    <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>springloaded</artifactId>
                        <version>${spring-loaded.version}</version>
                    </dependency>
                </dependencies>
            </plugin>
            <plugin>
                <groupId>org.liquibase</groupId>
                <artifactId>liquibase-maven-plugin</artifactId>
                <version>3.4.1</version>
                <configuration>
                    <propertyFile>src/main/resources/liquibase.properties</propertyFile>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

当我运行此测试时,我收到以下错误:

java.lang.NullPointerException
    at org.springframework.transaction.support.AbstractPlatformTransactionManager.rollback(AbstractPlatformTransactionManager.java:820)
    at org.unitils.database.transaction.impl.DefaultUnitilsTransactionManager.rollback(DefaultUnitilsTransactionManager.java:157)
    at org.unitils.database.DatabaseModule.rollbackTransaction(DatabaseModule.java:425)
    at org.unitils.database.DatabaseModule.endTransactionForTestMethod(DatabaseModule.java:390)
    at org.unitils.database.DatabaseModule$DatabaseTestListener.afterTestTearDown(DatabaseModule.java:486)
    at org.unitils.core.Unitils$UnitilsTestListener.afterTestTearDown(Unitils.java:315)
    at org.unitils.core.junit.AfterTestTearDownStatement.evaluate(AfterTestTearDownStatement.java:48)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.unitils.core.junit.BeforeTestClassStatement.evaluate(BeforeTestClassStatement.java:41)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)

看起来甚至没有达到测试代码。我做错了什么?

2 个答案:

答案 0 :(得分:3)

pom.xml中有两个数据库实现,没有指定范围。 Spring无法猜出哪一个是正确的,因此是例外。

    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
    </dependency>
    [...]
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>9.4-1206-jdbc42</version>
    </dependency>

这很可能会对您有所帮助:Configure specific in memory database for testing purpose in Spring

答案 1 :(得分:2)

由于&lt; 50 rep,我无法添加评论。我想也许这会很有用。

Rollback transaction after @Test

它不使用Unitils,但似乎确实是您正在寻找的行为。 Unitils似乎最近在2011年更新了(我怀疑这就是你的问题所在),所以它可能值得一试。