持續整合之路——資料訪問層的單元測試(續)

kjmeng發表於2013-07-15

        在上一篇中,完成了對測試用資料來源的配置。下面繼續構建可執行的測試。

        三、使用DBUnit管理資料

        測試的維護一直是我比較頭疼的問題,期望可以有一個比較易於維護和可複用的方法來管理這些資料。在沒有更好的方法之前,暫時選用DBUnit。(反思:其實我一直在為沒有發生的事情擔心,使得事情根本沒有進展。從已存在的、最簡單的地方入手,才是正確的處理方式。

        在pom.xml中引入dbunit和springtestdbunit包,後者提供通過註解方式使用DBUnit:

        <dependency>
            <groupId>org.dbunit</groupId>
            <artifactId>dbunit</artifactId>
            <version>2.4.9</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.github.springtestdbunit</groupId>
            <artifactId>spring-test-dbunit</artifactId>
            <version>1.0.1</version>
            <scope>test</scope>
        </dependency>

         DBUnit使用xml檔案管理資料集,通過使用第三方的庫也可以很方便的支援JSON格式。這裡使用xml:

<?xml version="1.0" encoding="utf-8"?>
<dataset>
    <building id="1" name="SOHO"/>
    <building id="2" name="New Gate Plaza"/>
    <floor id="1" floor_num="2" building="1"/>
    <floor id="2" floor_num="3" building="1"/>
    <floor id="3" floor_num="5" building="2"/>
</dataset>

         這個資料檔案放在了 /src/test/resources/中,與測試用例在同一個級別的目錄中。為了便於區分,我採用了:Dao類名-被測試的方法名-dataset.xml 的命名方式,例如:UserDao-findByname-dataxml.set。以後如果測試用例需要修改,就可以根據名字很方便地找到對應的資料集,並且不會影響其他測試用例。

         注意:

         1. 這裡的Element及其Attribute名稱要和資料庫的結構一一對應,而不是實體類。

         2. 如果同一個資料物件初始化時,需要初始化的欄位數目不一樣,比如:一條資料需要初始化的欄位是8個,而另外一個是4個。那麼一定要欄位數多的放在前面。

         四、編寫測試用例

         在編寫用例前,還是看下被測試的程式碼。用到的兩個實體類:

package com.noyaxe.myapp.entity;

import javax.persistence.Entity;
import javax.persistence.Table;

@Entity
@Table(name = "building")
public class Building extends IdEntity {
    private String name;


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

package com.noyaxe.myapp.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name = "floor")
public class Floor extends IdEntity {
    private Integer floorNum;
    private Building building;

    @Column(name = "floor_num")
    public Integer getFloorNum() {
        return floorNum;
    }

    public void setFloorNum(Integer floorNum) {
        this.floorNum = floorNum;
    }

    @ManyToOne(optional = false)
    @JoinColumn(name = "building")
    public Building getBuilding() {
        return building;
    }

    public void setBuilding(Building building) {
        this.building = building;
    }
}

        被測試的FloorDao:

package com.noyaxe.myapp.repository;

import com.noyaxe.myapp.entity.Floor;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.PagingAndSortingRepository;

import java.util.List;

public interface FloorDao extends JpaSpecificationExecutor<Floor>, PagingAndSortingRepository<Floor, Long> {
    public Floor findByBuildingNameAndFloorNum(String building, Integer floorNum);

    public List<Floor> findByBuildingName(String building);
}

      測試用例也十分簡單:

      

package com.noyaxe.myapp.repository;

import com.github.springtestdbunit.annotation.DatabaseSetup;
import com.noyaxe.myapp.entity.Floor;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.support.DirtiesContextTestExecutionListener;

import java.util.List;

import static junit.framework.Assert.assertNull;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext-test.xml")
@TestExecutionListeners({
        DependencyInjectionTestExecutionListener.class,
        DirtiesContextTestExecutionListener.class,
        TransactionDbUnitTestExecutionListener.class})
public class FloorDaoTest {
    @Autowired
    private FloorDao floorDao;

    @Test
    @DatabaseSetup("FloorDao-findbByBuidlingName-dataset.xml")

public void testFindByBuildingName(){ List<Floor> singleFloorList = floorDao.findByBuildingName(“SOHO”); assertEquals(1, singleFloorList.size()); List<Floor> twoFloorList = floorDao.findByBuildingName(“New Gate Plaza”); assertEquals(2, twoFloorList.size()); List<Floor> emptyFloorList = floorDao.findByBuildingName(“Test”); assertEquals(0, emptyFloorList.size()); } @Test

    @DatabaseSetup("FloorDao-findbByBuidlingNameAndFloorNum-dataset.xml")

public void testFindByBuildingNameAndFloorNum(){ Floor floor = floorDao.findByBuildingNameAndFloorNum(“SOHO”, 2); assertNotNull(floor); Floor empty = floorDao.findByBuildingNameAndFloorNum(“New Gate Plaza”, 7); assertNull(empty); empty = floorDao.findByBuildingNameAndFloorNum(“No Building”, 7); assertNull(empty); }}        通過程式碼,可以很清楚的看到通過DatabaseSetup完成了對測試資料的引入。這裡在每個測試方法前引入不同的檔案,如果所有的方法可以通過一個檔案包括,那麼也可以在類前面使用DatabaseSetup引入資料檔案。

        至此,一個完整的資料層測試用例已經呈現,並且可以執行。可是實際的過程卻並沒有這麼順利,接下來的文章就要總結一下遇到的問題。


相關文章