1. 程式人生 > >Java SE環境中測試JPA實體的簡單方法

Java SE環境中測試JPA實體的簡單方法

span utf AD als clear 編寫 before driver 我們

Java SE環境中測試JPA實體的簡單方法

出於軟件質量的考慮,理論上來說我們寫的一切代碼都要經過測試。JPA的測試不像普通的組件那麽方便,因為JPA涉及到數據庫,所以集成測試必不可少,像Arquillian這樣的測試框架能處理比較復雜的集成測試,但是它的配置相對也更復雜一點,所以本篇文章主要講一下在Java SE環境中較簡單地測試JPA實體(Entity)的方法。

我們需要實現的目標有:1.不需要mysql這樣需要額外安裝的數據庫;2.在SE環境中可以直接測試。

相關工具我們主要用到JUnit,Maven。相關源碼參考我的github倉庫。

添加依賴

pom.xml中添加如下依賴:

<dependency>
<groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <version>1.4.196</version>
<scope>test</scope> </dependency> <dependency> <groupId>org.hibernate.javax.persistence</groupId> <artifactId>hibernate-jpa-2.1-api</artifactId> <version>1.0.2.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId> <version>5.2.10.Final</version> </dependency>

我們用到了h2數據庫,jpa2.1-api以及hibernate。

JPA實體

假設我們要測試的實體為這樣(省略了getter/setter):

src/main/java/com/github/holyloop/entity/Book.java

@Entity
@Table(name = "book")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(unique = true, nullable = false)
    private Long id;

    @Column(nullable = false)
    private String title;

    @Column(nullable = false)
    private String author;
}

持久化單元

在測試資源中添加持久化單元聲明:

src/test/resources/META-INF/persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1"
  xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="
        http://xmlns.jcp.org/xml/ns/persistence
        http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">

  <persistence-unit name="test" transaction-type="RESOURCE_LOCAL">

    <class>com.github.holyloop.entity.Book</class>

    <properties>
      <!-- Configuring JDBC properties -->
      <property name="javax.persistence.jdbc.url"
        value="jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;INIT=RUNSCRIPT FROM ‘classpath:create.sql‘\;
                RUNSCRIPT FROM ‘classpath:data.sql‘" />
      <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />

      <!-- Hibernate properties -->
      <property name="hibernate.archive.autodetection" value="class, hbm" />
      <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
      <property name="hibernate.format_sql" value="true" />
      <property name="hibernate.show_sql" value="true" />

    </properties>
  </persistence-unit>
</persistence>

這裏給持久化單元命名為test,添加了h2數據庫驅動,並且聲明了幾個hibernate專有的屬性,它們將輸出一些
格式化的調試信息方便排查問題。

測試類

SQL

首先我們來添加幾個sql文件:

src/test/resources/create.sql:

-- book
drop table if exists `book`;
create table book (
  id bigint(20) unsigned not null auto_increment,
  title varchar(50) not null,
  author varchar(20) not null,
  primary key (id)
);

src/test/resources/data.sql:

DELETE FROM book;
INSERT INTO book(id, title, author) VALUES (1, ‘Spring in Action‘, ‘Craig Walls‘);

當然你也可以在persistence.xml中聲明:

<property name="hibernate.hbm2ddl.auto" value="create-drop" />

而不用create.sql文件。上面這個屬性將會自動地將我們的Entity聲明轉換為對應的ddl,並且結束後會刪除數據
。我這邊用create.sql只是出於個人喜好。

data.sql中可以隨意地插入測試數據。

Java

先看看我們的基本測試類:

src/test/java/com/github/holyloop/entity/BaseTest.java:

protected static EntityManagerFactory emf;
protected static EntityManager em;

@BeforeClass
public static void init() {
  emf = Persistence.createEntityManagerFactory("test");
  em = emf.createEntityManager();
}

@AfterClass
public static void tearDown() {
  em.clear();
  em.close();
  emf.close();
}

這裏聲明了實體管理器工廠emf和實體管理器em,inittearDown分別在測試類初始化和銷毀時執行,init負
責初始化實體管理器em,tearDown則釋放對應資源。我們繼續在基本測試類中添加如下方法:

@Before
public void initDB() {
  Session session = em.unwrap(Session.class);
  session.doWork(new Work() {
    @Override
    public void execute(Connection connection) throws SQLException {
      try {
        File script = new File(getClass().getResource("/data.sql").getFile());
        RunScript.execute(connection, new FileReader(script));
      } catch (FileNotFoundException e) {
        throw new RuntimeException("could not initialize with script");
      }
    }
  });
}

@After
public void clean() {
  em.clear();
}

這樣我們的所有子類測試方法在開始之前都會預加載data.sql中的數據,並且結束後實體管理器將清除掉持久化上下文,這樣保證測試方法之間不會互相影響。

接下來我們實現一個簡單的測試:

src/test/java/com/github/holyloop/entity/BookTest.java:

public class BookTest extends BaseTest {

    @Test
    public void testAddBook() {
        Book book = new Book();
        book.setTitle("new book");
        book.setAuthor("new author");

        em.getTransaction().begin();
        em.persist(book);
        em.getTransaction().commit();

        @SuppressWarnings("rawtypes")
        List books = em.createQuery("select b from Book b").getResultList();
        assertEquals(2, books.size());
    }

    @Test
    public void testQueryBook() {
        Book book = em.find(Book.class, 1L);
        assertNotNull(book);
    }

}

BookTest 繼承了我們上面實現的基本測試類,這裏我實現了兩個基本測試,testAddBook測試能否成功添加一本新書,testQueryBook測試能否查找到我們在data.sql中插入的測試數據。

測試

測試用例編寫完畢之後可以開始測試了,進入到你的項目根目錄,some-path/your-project:

執行:

mvn clean test

如果一切正常,測試結果如下:
技術分享圖片

以上就是不額外安裝數據庫,不依賴其他容器的測試JPA實體的方法。

Java SE環境中測試JPA實體的簡單方法