單元測試之EmbeddedPostgres

當我們做DAO層測試時,一般都是做單元測試,而不是像集成測試那樣把項目運行起來。本章就是基於Postgres來講講如何做DAO層的單元測試。

一、DBUtility

單元測試因爲不能運行項目,所以我們需要自己實現一個嵌入式的數據庫的運行和關閉。DBUtility類就是做這個工作的。

import com.opentable.db.postgres.embedded.EmbeddedPostgres;

import java.io.IOException;

public class DBUtility {

    private static EmbeddedPostgres embeddedPostgres;

    private static final int DB_PORT = 5435;

    private DBUtility() {};


    public synchronized static EmbeddedPostgres startDatabase(){
        try {
            if(embeddedPostgres == null) {
                embeddedPostgres = EmbeddedPostgres.builder()
                        .setPort(DB_PORT).start();
            }
        } catch (IOException e) {
            throw new RuntimeException("Failed initializing embedded postgres ");
        }

        return embeddedPostgres;
    }

    public synchronized static void shutdownDatabase() throws IOException {
        if(embeddedPostgres != null){
            embeddedPostgres.close();
        }
    }

    public synchronized  boolean isDatabaseStarted(){
        return embeddedPostgres != null;
    }

}

二、JpaPersistConfig

然後,我們需要配置DataSource和JpaTransactionManager等。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;
import java.util.Properties;

@Configuration
@EnableJpaRepositories(basePackages = "xxx.xxx.xxx.xxx.repository")
@PropertySource("classpath:jpa-persistent.properties")
@EnableTransactionManagement
public class JpaPersistConfig {

    @Autowired
    private Environment env;

    @Bean
    public DataSource dataSource() {
        final DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("jdbc.url"));
        dataSource.setUsername(env.getProperty("jdbc.username"));
        dataSource.setPassword(env.getProperty("jdbc.password"));

        return dataSource;
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
        final LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource());
        em.setPackagesToScan(new String[] { "xxx.xxx.xxx.xxx.entity" });
        em.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        em.setJpaProperties(additionalProperties());
        return em;
    }

    @Bean
    JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }

    final Properties additionalProperties() {
        final Properties hibernateProperties = new Properties();

        hibernateProperties.setProperty("hibernate.jdbc.lob.non_contextual_creation", env.getProperty("hibernate.jdbc.lob.non_contextual_creation"));
        hibernateProperties.setProperty("hibernate.dialect", env.getProperty("hibernate.dialect"));
        hibernateProperties.setProperty("hibernate.show_sql", env.getProperty("hibernate.show_sql"));
        hibernateProperties.setProperty("hibernate.cache.use_second_level_cache", env.getProperty("hibernate.cache.use_second_level_cache"));
        hibernateProperties.setProperty("hibernate.cache.use_query_cache", env.getProperty("hibernate.cache.use_query_cache"));

        return hibernateProperties;
    }
}

其中,@EnableJpaRepositories用來掃描和發現指定包及其子包中的Repository定義;@EnableTransactionManagement 用來開啓事務支持;@PropertySource 用來加載外部資源文件。

jpa-persistent.properties 如下所示:
jdbc.driverClassName=org.postgresql.Driver
jdbc.url=jdbc:postgresql://localhost:5435/postgres
jdbc.username=postgres
jdbc.password=postgres

hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
hibernate.jdbc.lob.non_contextual_creation=true
hibernate.show_sql=true
hibernate.cache.use_second_level_cache=false
hibernate.cache.use_query_cache=false

三、AbstractJPATest

AbstractJPATest是所有DAO層測試類的父類。

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.jdbc.Sql;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.AnnotationConfigContextLoader;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { JpaPersistConfig.class }, loader = AnnotationConfigContextLoader.class)
@Transactional
@DirtiesContext
@Sql(scripts = {"classpath:schema.sql", "classpath:test-data.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
public abstract class AbstractJPATest {

    @BeforeClass
    public static void beforeClass() throws Exception{
       DBUtility.startDatabase();
    }

    @AfterClass
    public static void afterClass() throws Exception {
        // Suite測試已經關閉了,這裏註釋掉
        // DBUtility.shutdownDatabase();
    }
}

其中,@BeforeClass和@AfterClass分別指定了測試運行時啓動和關閉數據庫。@Transactional表示使用事務,@DirtiesContext表明在整個測試結束後關閉該測試的Application context並清除緩存。而@Sql註解的executionPhase參數使用BEFORE_TEST_METHOD,表明在測試方法運行前執行,scripts指定執行的腳本,也就是創建數據表和初始數據。@ContextConfiguration註解用來標註我們想要導入到這個測試類的某些bean,這裏也就是JpaPersistConfig中配置好的DataSource等。

四、實際測試類

實際測試類只需繼承AbstractJPATest,即可注入Repository,來進行單元測試了。

import com.scb.xxx.xxx.xxx.model.CustomerInfo;
import com.scb.xxx.xxx.repository.EBBSCustomerTempRepository;
import com.scb.xxx.xxx.repository.entity.EBBSCustomerTemp;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

import java.time.LocalDateTime;
import java.util.Optional;

public class EBBSCustomerTempRepoTest extends AbstractJPATest {
    @Autowired
    private EBBSCustomerTempRepository repository;

    @Test
    public void testFindEBBSCustomerTemp() {
        Optional<EBBSCustomerTemp> customerTemp = repository.findById(3L);
        Assert.assertNotNull(customerTemp.get());
        Assert.assertEquals("xxxxx", customerTemp.get().getRelationshipNo());
        Assert.assertEquals(1, customerTemp.get().getTaskNo());
        //repository.findAll().stream().forEach(System.out::println);
    }

    @Test
    public void testSaveEBBSCustomerTemp() {
        EBBSCustomerTemp customerTemp = new EBBSCustomerTemp();
        customerTemp.setRelationshipNo("xxxxx");
        customerTemp.setCreateTime(LocalDateTime.now());
        customerTemp.setTaskNo(2);
        CustomerInfo info = new CustomerInfo();
        info.setArmCode("xxx");
        info.setRelationshipType("xxx");
        info.setSegmentCode("61");
        info.setSubSegmentCode("xx");
        customerTemp.setCustomerInfo(info);

        EBBSCustomerTemp saved = repository.save(customerTemp);
        Assert.assertNotNull(saved.getId());
        Assert.assertNotNull(saved.getCustomerInfo());
        Assert.assertEquals("xxxx", saved.getRelationshipNo());
        Assert.assertEquals("xxx", saved.getCustomerInfo().getArmCode());
        Assert.assertEquals(2, saved.getTaskNo());
    }

    @Test
    public void testFindEBBSCustomerTempWithPageable() {
        int pageNo = 0;
        int pageSize = 2;
        Pageable pageable = PageRequest.of(pageNo, pageSize);
        Page<EBBSCustomerTemp> tempPage = repository.findByTaskNo(pageable, 1);
        tempPage.get().forEach(System.out::println);
        int totalPage = tempPage.getTotalPages();
        long totalElement = tempPage.getTotalElements();
        Assert.assertEquals(3, totalPage);
        Assert.assertEquals(5, totalElement);
        Assert.assertEquals("xxx", tempPage.getContent().get(0).getRelationshipNo());
        Assert.assertEquals("xxx", tempPage.getContent().get(1).getRelationshipNo());
        while (tempPage.hasNext()) {
            Pageable nextPageable = tempPage.nextPageable();
            Page<EBBSCustomerTemp> nextTempPage = repository.findByTaskNo(nextPageable, 1);
            nextTempPage.get().forEach(System.out::println);
            Assert.assertNotNull(nextTempPage.getContent());
            Assert.assertEquals(false, nextTempPage.getContent().isEmpty());
            tempPage = nextTempPage;
        }
    }
}

五、Suite測試

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.runner.RunWith;
import org.junit.runners.Suite;

import java.io.IOException;

@RunWith(Suite.class)
@Suite.SuiteClasses({
        xxxTest.class,
        xxxRepoTest.class,
})
public class DAOTestSuite {

    @BeforeClass
    public static void beforeClass(){
        DBUtility.startDatabase();
    }

    @AfterClass
    public static void afterClass() throws IOException {
        DBUtility.shutdownDatabase();
    }
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章