先吐个槽,参加过很多技术大会,也看过个很多技术类文章,发现大部分存在一个通病,即:都会提问题,提思路,但是都不会讲具体的落地方案,所以我写东西给自己定了一个目标,即:能够落地,尽量提供一个小而简单的 Demo 让感兴趣的同学能快速上手。
好了,这里啰嗦两句,下面进入正题。
在上两篇中,我们先介绍了需求功能,然后讲解了大概的框架设计,今天这篇主要看用例管理功能怎么落地去实现。
走进Java接口测试之从0到1搭建数据驱动框架(需求篇)
走进Java接口测试之从0到1搭建数据驱动框架(设计篇)
这里使用的 IDE 是 IntelliJ IDEA 2018
:
引包,配置 pom.xml:
<dependencies>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.1version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
<dependency>
<groupId>org.testnggroupId>
<artifactId>testngartifactId>
<version>6.14.3version>
<scope>compilescope>
dependency>
dependencies>
├─logs
│ └─spring-boot-logback # 日志文件
│ all_api-test-logback.log # 所有日志
│ err_api-test-logback.log # 错误日志
├─src
│ ├─main
│ │ ├─java
│ │ │ └─com
│ │ │ └─zuozewei
│ │ │ └─springbootdatadrivendemo
│ │ │ │ SpringbootDataDrivenDemoApplication.java # 启动类
│ │ │ │
│ │ │ ├─db
│ │ │ │ ├─auto # 存放MyBatis Generator生成器生成的数据层代码,可以随时删除再生成
│ │ │ │ │ ├─mapper # DAO 接口
│ │ │ │ │ └─model # Entity 实体
│ │ │ │ └─manual # 存放自定义的数据层代码,包括对MyBatis Generator自动生成代码的扩展
│ │ │ │ ├─mapper # DAO 接口
│ │ │ │ └─model # Entity 实体
│ │ │ ├─handler # 数据转换
│ │ │ └─service # 业务逻辑
│ │ │ └─impl # 实现类
│ │ │
│ │ └─resources
│ │ │ application.yml # 全局配置文件
│ │ │ generatorConfig.xml # Mybatis Generator 配置文件
│ │ │ logback-spring.xml # logback 配置文件
│ │ │ spy.properties # P6Spy 配置文件
│ │ │
│ │ ├─db
│ │ ├─mapper
│ │ │ └─com
│ │ │ └─zuozewei
│ │ │ └─springbootdatadrivendemo
│ │ │ └─db
│ │ │ ├─auto # 存放MyBatis Generator生成器生成的数据层代码,可以随时删除再生成
│ │ │ │ └─mapper # 数据库 Mapping 文件
│ │ │ │
│ │ │ └─manual # 存放自定义的数据层代码,包括对MyBatis Generator自动生成代码的扩展
│ │ │ └─mapper # 数据库 Mapping 文件
│ │ └─testng
│ │ │ APICollection-TestSuite.xml # 所用测试用例集
│ │ └─jdbcbapi
│ │ jdbcAPI-TestSuite.xml # 某API测试用例集
│ │
│ └─test
│ └─java
│ └─com
│ └─zuozewei
│ └─springbootdatadrivendemo
│ └─demo # 接口测试用例
├─pom.xml
创建测试用例表:
CREATE DATABASE /*!32312 IF NOT EXISTS*/`autotest` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_vietnamese_ci */;
USE `autotest`;
/*Table structure for table `api_testdata_demo` */
DROP TABLE IF EXISTS `api_testdata_demo`;
CREATE TABLE `api_testdata_demo` (
`id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '测试ID',
`Protocol` enum('Http','RPC','jdbc') DEFAULT NULL COMMENT '协议',
`Category` enum('Webapi','db') DEFAULT NULL COMMENT '接口类别',
`Method` varchar(128) DEFAULT NULL COMMENT '接口名称',
`Parameters` varchar(1000) DEFAULT NULL COMMENT '参数',
`expected` varchar(128) DEFAULT NULL COMMENT '检查点',
`description` varchar(1000) DEFAULT NULL COMMENT '描述',
`isRun` enum('1','0') DEFAULT NULL COMMENT '运行状态,1:运行,0:未运行',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
/*Data for the table `api_testdata_demo` */
insert into `api_testdata_demo`(`id`,`Protocol`,`Category`,`Method`,`Parameters`,`expected`,`description`,`isRun`) values (1,'jdbc','db','demo','latte','CNY 25.00','测试demo','1');
创建完成大概是这样:
这里的 SQL 主要决定了选取哪些测试用例进行测试:
SELECT * FROM autotest.api_testdata_demo
WHERE Protocol = 'jdbc'
AND Category = 'db'
AND Method = 'demo'
AND isRun = 1;
注意:SQL 取用例是非常灵活,可以根据自己的业务调整表结构。
这里使用 mybatis 直接使用原生的 SQL 查询测试用例的数据。
MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
编写对应的 TestDataMapper.xml:
<mapper namespace="com.zuozewei.springbootdatadrivendemo.db.manual.mapper.TestDataMapper" >
<select id="selectBysql" parameterType="String" resultType="java.util.LinkedHashMap">
${value};
select>
mapper>
注意:
${}
,而不是#{}
,这是因为如果我们使用 #{}
的时候,MyBatis 会自动帮我们把我们的字段自动加上单引号’,使用 ${}
的时候就不会;java.util.LinkedHashMap
作为返回类型,可以保持结果集本来的字段顺序。dao 层增加 TestDataMapper.java:
/**
* 描述:
* 自定义sql查询
*
* @author zuozewei
* @create 2019-11-21 21:18
*/
public interface TestDataMapper {
// 自定义sql查询
List<LinkedHashMap<String, Object>> selectBysql(String sql);
}
Service 的接口 TestDataService :
/**
* 描述: TestDataService
*
* @author zuozewei
* @create 2019-11-21 18:00
*/
public interface TestDataService {
// 自定义查询
List<LinkedHashMap<String, Object>> selectBysql(String sql);
}
实现 Service 的接口调用方法:
/**
* 描述: 参数化自定义查询实现类
*
* @author zuozewei
* @create 2019-11-21 16:04
*/
@Service
public class TestDataServiceImpl implements TestDataService {
@Resource
private TestDataMapper testDataMapper;
@Override
public List<LinkedHashMap<String, Object>> selectBysql(String sql) {
return testDataMapper.selectBysql(sql);
}
}
为了避免出现值 null 的列,不能被保存到 LinkedHashMap 对象 中,需要在 application.yml
的配置文件中 mybatis 如下配置:
mybatis:
configuration:
call-setters-on-nulls: true # 调用setter null,返回空也必须设置到bean中(直接执行sql专用)
脚本参数化主要使用 TestNG 的 @DataProvider
& Testng.xml
首先我们在resource下创建一个 testng 配置文件:
<suite name="jdbc 测试" verbose="1" preserve-order="true" >
<test name="测试demo" preserve-order="true">
<parameter name="sql"
value="SELECT * FROM autotest.api_testdata_demo
WHERE Protocol = 'jdbc'
AND Category = 'db'
AND Method = 'demo'
AND isRun = 1;"/>
<classes>
<class name="com.zuozewei.springbootdatadrivendemo.demo.TestMapperService"/>
classes>
test>
suite>
解释一下配置文件:
编写脚本代码 TestMapperService.java:
@SpringBootTest
@Slf4j
public class TestMapperService extends AbstractTestNGSpringContextTests {
private String sql; // SQL参数
@Autowired
private TestDataService testDataService;
@Parameters({"sql"})
@BeforeClass
public void beforeClass(String sql) {
this.sql = sql;
}
/**
* XML中的SQL决定了执行什么用例, 执行多少条用例, SQL的搜索结果为需要测试的测试用例
*/
@DataProvider(name = "testData")
private Object[][] getData() {
List<LinkedHashMap<String, Object>> results = testDataService.selectBysql(sql);
Object[][] objects = new Object[results.size()][];
for (int i = 0; i < results.size(); i++) {
objects[i] = new Object[]{results.get(i)};
}
return objects;
}
@Test(dataProvider = "testData",description = "测试demo")
public void testSelect(Map<String, String> data) throws InterruptedException {
// to do something...
}
}
注意:
AbstractTestNGSpringContextTests
,如果不继承AbstractTestNGSpringContextTests,会导致 @Autowired 不能加载 Bean。在今天这篇文章中,主要基于 SpringBoot 框架的能力,和大家分享了实现一个用例管理的过程。在实现过程中,你最需要关注的几部分内容是:
至此,我们要实现接口用例集中式管理功能,也算是完成了。