开发者测试(未完待续)

开发者测试

代码分析:

以典型的微服务为例,分Controller,Service,Dao(Mybatis Mapper),适配层,每一层都有各自的特点:

Controller:代表的是Restful API的出入口,Request为入数据,Response为出数据。看护好输入输出可以有效的看护好API层的能力。常规的自动化测试也在这个领域耕耘较多,如各种API Test的工具,包括PostMan等。在开发和测试团队分离的团队里面,测试主要聚焦此类的自动化测试。

Service层:该层的API,定位于基于业务模型的封装,为Controller提供能力业务模型的源自能力或者组合能力。为

Dao层:数据库操作的原子能力。与真实数据库交互验证,对环境依赖比较重。

适配层:与其他微服务组件交互的能力封装。供Service层消费。

常见问题

1、没有开发者测试,最典型:开发写完代码,交给测试同学测试。至于测试体系的构建,那是测试的事情。

2、迫于指标压力,业务代码和单元测试代码是两拨人写的,测试数据有效性较差,常见于完成任务。

3、理解开发者测试的重要性,奈何工具方法选择不当,导致在开发者测试花费的经历,不小于业务代码的开发维护工作维护工作量

经验分享

分享几条经验原则:

何为初心:为什么要做开发者测试?试想:在开发代码阶段,假定自定开发的业务代码没有测试同学来给自己测试,那么自己应该怎么保证整理,一两个需求可以手工测试,但日积月累数月,甚至数年。体力有限,想必也不可能完全充分验证,当然年轻力壮着除外。那么如何保障质量?

何得始终

1、测试代码架构也需要设计,如何设计适合自己业务特征的单元测试框架。

2、选择合适的测试库,测试方法。每个人分享的看法经验都是个人的经历总结,都不具备普适性,不是公式,可以套用完事。

比如:“单元测试重点测试本类,本方法。被调用方法的测试由被调用方法的单元测试保证”,说的也没问题,但是是不是适合你?也不一定,自己琢磨

如何选择:

不说废话了,干正事

一、Controller层测试:

1、如果你的项目是Spring系列的,建议SpringRunner体系的单元测试。

优势:依赖注入,灵活的Mockbean体系,只需要mock掉该API业务中涉及的适配层调用第三方的API的打桩即可,做最少的桩,测试最深的代码路径。

劣势:需要启动Spring工程,速度略慢

二、Service层测试:

1、测试框架选型
选择1、SpringRunner

依然可以使用SpringRunner体系的单元测试。优劣势参考Controler层测试的分析。

选择2、Mockito、PowerMock

优势:运行速度快,测试灵活

劣势:不可控因素较多,尤其碰到有一些框架做了jar包隔离,有些类方法就不能用了,会出现NoClassDefine的错误。

PowerMock相比于Mockito,其支持Mock静态方法,私有方法,私有变量。看似强大,但建议,对于静态,私有方法,私有变量能不Mock就不Mock,尽可能还原业务本质。

2、有效的Mock和验证手段
2.1、参数校验器

可以验证mock对象的doSomething方法被调用的时候,参数的值是不是期望值

ArgumentCaptor<Person> argument = ArgumentCaptor.forClass(Person.class);
verify(mock).doSomething(argument.capture());
assertEquals("zhaosi", argument.getValue().getName());
2.2、函数调用次数
verify(mockList, times(1)).size(); //调用1次
verify(mockList, atLeastOnce()).size(); // 大于1次调用
verify(mockList, atMost(2)).size(); // 最多2次调用
verify(mockList, atLeast(1)).size(); // 最少1次调用
verify(mockList, never()).clear(); // 从来没调用过

三、Dao(Mybatis Mapper)

使用H2内存数据库做单元测试。

1、bean的配置文件

创建test-spring-mybatis.xml配置文件,后面讲会加载该配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <aop:aspectj-autoproxy expose-proxy="true" proxy-target-class="true"/>

    <!-- 主要定义java里面mapper接口路径,如果存在多个路径,则“;”分割 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.xxx.domain.*.teamdbrepository;com.xxx.domain.*.repository" />
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
    </bean>

    <!-- 主要定义mapper.xml的文件路径,存在多个路径,则多条记录即可 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="helloDatasource"/>
        <property name="mapperLocations">
            <array>
                <value>classpath:sqlmap/other/*.xml</value>
                <value>classpath:sqlmap/team/*.xml</value>
            </array>
        </property>
    </bean>

    <bean id="helloDatasource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="org.h2.Driver" />
        
        <!-- 配置为h2以mysql模式在文件中运行,方便调试使用 -->
<!--        <property name="url" value="jdbc:h2:file:D://hellodb;MODE=MYSQL;DB_CLOSE_DELAY=-1" />-->
	
        <!-- 配置为h2以mysql模式在内存中运行 -->
        <property name="url" value="jdbc:h2:mem:hellodb;MODE=MYSQL;DB_CLOSE_DELAY=-1" />
    </bean>
    
    <!-- 初始化数据库的建表脚本 -->
    <jdbc:initialize-database data-source="helloDatasource" ignore-failures="DROPS">
        <jdbc:script location="classpath*:hellodb*.sql" />
    </jdbc:initialize-database>
    <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>
    <bean id="transactionManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="helloDatasource"/>
    </bean>
</beans>
2、加载test-spring-mybatis.xml配置
public class MyBatisTest {

    protected static ApplicationContext context;

    public static ApplicationContext getContext() {
        if (context == null) {
            context = new ClassPathXmlApplicationContext("classpath:META-INF/spring/test-spring-mybatis.xml");
        }
        return context;
    }
}
3、Mapper单元测试实例
public class TeamServiceScopeMapperTest {

    PersonMapper personMapper = MyBatisTest.getContext().getBean(PersonMapper.class);

    @Test
    public void testServiceScopeMapper() {
        PersonMapper personMapper = new PersonMapper();
        Person person = new Person();
        person.setId(1);
        personMapper.insert(person);

        Person person = personMapper.selectByPrimaryKey(1);
        Assert.assertNotNull(person);
        Assert.assertEquals(person.getId, "1");
    }
}
4、Mysql和H2的兼容性

虽然H2支持以MYSQL模式运行,但是在DDL以及一些深度Mybatis的函数上是不能支持的。差异主要如下:

注释

H2不支持表级Comment。

索引

MySql要求表级索引名称必须唯一,H2要求库级别索引必须唯一。也就是如果业务数据表设计的时候,Index在多个表中有重复,那在H2会出现Index重复错误

各种时间类函数

比如UTC_TIMESTAMP()函数,如果Mapper中有此类函数,在H2中会失败。可以在DDL中定义函数别名,在执行mapper中对应函数时,代理到java代码实现MySql 函数的支持。做如下两部:

1、在DDL分两部分,如下

-- 函数映射部分,下例
CREATE ALIAS IF NOT EXISTS UTC_TIMESTAMP FOR "com.togo.mybatis.H2DateFunction.utcTimestamp";

-- 建表SQL部分, 下例
DROP TABLE IF EXISTS `t_person`;
CREATE TABLE `t_person`  (
                           `id` bigint(20) NOT NULL ,
                           `name` varchar(64)  NOT NULL 
);

2、在com.togo.mybatis.H2DateFunction中定义utcTimestamp函数,如

public static LocalDateTime utcTimestamp() {
    return ZonedDateTime.now(ZoneId.of("UTC")).toLocalDateTime();
}

引申:

1、数据驱动测试

待补充

2、Testng测试套

待补充

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值