单元测试之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();
    }
}

 

 

 

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