zoukankan      html  css  js  c++  java
  • 如何编写单元测试-基于Spring

    单元测试

    首先单元测试真的算是一种“脏活累活”,但是我个人感觉还是有必要,至少本人最近开始写单元测试后还是能发现一些“bug”的。

    如何写单元测试

    单元测试的要求...网上很多。下面来分享一下我是如何写单元测试。首先我们项目一般都是MVC分层的,而单元测试主要是在Dao层和Service层上进行编写。从项目结构上来说,Service层是依赖Dao层的,但是从单元测试角度,对某个Service进行单元的时候,他所有依赖的类都应该进行Mock。而Dao层单元测试就比较简单了(下面Dao层就以Jdbc为例子),只依赖数据库中的数据。

    DAO层单元测试。

    dao层单元测试肯定要是连数据库的,但是不需要本地起一个Mysql,可以使用H2内存数据库来做单测就足够了。

    实战

    首先给一个通用模板吧,就是所有DAO层单元测试都会用到的,注:ORM使用Mybatis

    @RunWith(SpringJUnit4ClassRunner.class)public class XXXDaoTest {
    
        @MapperScan({"com.xxx.xxx.mapper"})
        @Configuration
        public static class MybatisScanConfiguration {
    
            @Bean
            public XXXDao xxxDao() {
                return new XXXDaoImpl();
            }
    
            @Bean
            public DataSource h2DataSource() {
    
                EmbeddedDatabaseBuilder builder = new EmbeddedDatabaseBuilder();
                EmbeddedDatabase database = builder.setType(EmbeddedDatabaseType.H2)
                        .addScript("classpath:/xxx/init_table.sql") //启动时初始化建表语句
                        .build();
                return database;
            }
    
            @Bean
            public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
                final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
                sessionFactory.setDataSource(dataSource);
                PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
                //加载所有的sql mapper文件
                Resource[] mapperLocations = resolver.getResources("classpath:com/xxx/xxx/XXXXMapper.xml");
                sessionFactory.setMapperLocations(mapperLocations);
                return sessionFactory.getObject();
            }
    
            @Bean
            public PlatformTransactionManager platformTransactionManager(DataSource dataSource) {
                return new DataSourceTransactionManager(dataSource);
            }
    
            @Bean
            public JdbcTemplate jdbcTemplate(DataSource dataSource) {
                return new JdbcTemplate(dataSource);
            }
        }
    @Autowired
    XXXDao xxxDao
      @Test
      @Transactional
      public void xxxx() {

      }
    }

    其中的一些需要稍微修改的,我使用“XXX”或者“xxx”,如果配过Spring的肯定知道怎么弄。其中init_table.sql就是用来创建数据库表的,就不列出来的,下面需要注意的地方:

    1. H2 DataBase有些语法是不支持的,例如 insert ignore,还有类似在创建数据库时候"CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"
    2. 如果一些sql是Mapper不提供的,那么可以使用jdbcTemplate来执行
    3. 每次单元测试执行完后,建议删除当前单元测试方法上,加上 @Transactional 注解,保证每次执行完后数据回滚
    4. 如果当前dao层存在其他依赖,其实是可以mock的(可以参考后面Service层的单元测试)

    通用模板写好以后,就可以按照不同情况来写单测了,想想是不是很开心啊

        @Test
       @Transactional
    public void testUpdatexxx() { Long testUserId = 1L; //1.当数据不存在的时候,返回0 int i = xxxDao.updateXxx(testUserId,1); Assert.assertTrue(i == 0); // 2.如果存在记录,状态是已经完成 返回0 Domain domain = createBy(testUserId, 0); //插入一条数据 mapper.insertSelective(domain); Assert.assertTrue(xxxDao.updateXxx(testUserId,1) == 0); //3.如果存在记录,状态是未完成 返回1 .... }

    Service层单元测试

    说实话,Service层的单测是最麻烦的,特别是那种超级大方法。Service层的单元测试就是主要Mock其他依赖Bean来完成各种逻辑的判断。

    实战

    还是先给一个模板

    @RunWith(SpringRunner.class)
    @ContextConfiguration(classes = NeedTestService.Config.class)
    public class NeedTestServiceTest {
    
        @TestConfiguration
        public static class Config {
            //这里直接创建一个待测试的类
            @Bean
            public NeedTestService needTestService() {
                return new NeedTestService();
            }
        }
        
        /*
         * Mock掉依赖的Bean
         */
        @MockBean
        private X1Dao x1Dao;
    
        @MockBean
        private X1Service x1Service;
    
     
        //这里是Autowired需要测试的类
        @Autowired
        private NeedTestService needTestService;
        
    }

    通用模板在上面,当然,遇到恶心的类,Mock十几个Bean也是有可能的。 下面就开始开心的写单元测试的了,例如

     @Test
        public void testCalculateUserClaims1() {
            Long testUserId = 1L;
            Date transTime = DateUtil.getStartDate(new Date());
            given(x1Dao.insertInitIgnore(testUserId, transTime)).willReturn(1);
    
            List<X2> list = Lists.newArrayList();
            list.addAll(createTenderCell(1L, "100.00", 10));
            list.addAll(createTenderCell(2L, "200.00", 5));
            Collections.shuffle(list);
            given(x1Dao.selectxxx(testUserId, transTime)).willReturn(list);
    
            
            ... 把改情况下 各种依赖的方法调用返回mock出来,
    
            //这里直接调用被测试的方法
            Result result = needTestService.needTestMethod(...)
            Assert.assertTrue(result != null);
            //... 省略掉各种 Assert.
           
        }

     注:一般单元测试可以分为:生成数据,mock数据,假设校验

    当然,这仅仅是一种情况,还有各种情况需要您进行测试,good luck!

    小结

    我也是最近才开始写单元测试的,虽然很无脑,但是我个人还是觉得有必要的,一些很低级的错误至少能找出来(如果你稍微认真的写,而不是敷衍)。这样总比在Code Review的时候被指出来要好很多吧。而且写单元测试也算是一种休息吧!

  • 相关阅读:
    973. K Closest Points to Origin
    919. Complete Binary Tree Inserter
    993. Cousins in Binary Tree
    20. Valid Parentheses
    141. Linked List Cycle
    912. Sort an Array
    各种排序方法总结
    509. Fibonacci Number
    374. Guess Number Higher or Lower
    238. Product of Array Except Self java solutions
  • 原文地址:https://www.cnblogs.com/lizo/p/7899706.html
Copyright © 2011-2022 走看看