概述
开发者测试
代码分析:
以典型的微服务为例,分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测试套
待补充
最后
以上就是轻松小笼包为你收集整理的开发者测试(未完待续)开发者测试的全部内容,希望文章能够帮你解决开发者测试(未完待续)开发者测试所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复