快嘉sdkg使用手册(二) test sdk实践指南 - fastjrun/sdkg GitHub Wiki

在快嘉sdkg使用手册(一)--小试牛刀中,我们下载了sdkg源码,本地仓库安装了快嘉sdkg-helper插件和快嘉single-sdk-archetype脚手架;接下来用single-sdk-archetype脚手架新建了一个test-sdk工程,并用sdkg-helper插件生成接口sdk代码和接口单元测试代码,然后用命令行直接执行了接口测试,输出测试报告。

test-sdk首先是一个接口sdk,该sdk可以直接应用在实际的项目中;其次test-sdk所有测试用例代码均是基于testng编写,既可以有效支持开发过程中的单元测试,也能够支持任意环境的接口联调、测试工作,从某种程度来看,test-sdk项目也可以被作为一个接口自动化测试框架的有效实践。

test-sdk使用testng来作为自己的单元测试框架,这使得它的单元测试用例代码不仅能够适用于开发阶段,还可以复用到后续的联调、持续测试和自动化测试等阶段,代码利用率的利用成都非常高;此外,又因为testng支持代码和数据分离,可以对同一套测试用例代码灌入多套测试数据,进一步提高代码利用率;最后,结合maven的profile和maven-surefire-plugin插件,可以针对不同的环境进行自动化测试。

test-sdk项目对应的接口定义参见:http://www.apihelp.cn/swagger-ui.html

本节不讲如何在具体项目中使用test-sdk,只谈它基于testng编写测试用例代码带来的具体好处并如何实现。

  • 支持IDE中单元测试
  • 支持@DataProvider参数注入,单元测试用例代码和测试数据分离
  • 支持同一个单元测试用例执行多套测试数据
  • 支持命令行执行,与maven-surefire-plugin无缝集成
  • 支持通过配置文件testng.xml,配置需要测试的class、method
  • 自带多线程,支持轻量的性能测试,有助于在开发联调阶段发现问题
  • 结合maven的profile使用testng的Parameters可以轻松实现test-sdk对接多套环境,执行自动化测试

1 支持IDE中单元测试 

1.1 IDE准备(eclipse和idea都支持) 

eclipse中安装testng插件

 eclipse的testng插件需要单独下载,我使用的eclipse是oxygen版本,使用的方式是从浏览器打开(http://marketplace.eclipse.org),搜索testng,显示如下

选中"TestNG for Eclipse"插件下的install按钮,直接拖到eclipse窗口中,会激活eclipse安装插件的流程,一路next下去即可。

 在Preferences窗口中找到TestNG->Run/Debug,JVM args中输入-DenvName=local(如果不这么操作的话,从eclipse中执行testng测试用例会报"Parameter 'envName' is required"),操作示意图如下

idea中默认安装是含有TestNg插件,只需要按下图配置jvm参数-DenvName=local即可(如果不这么操作的话,从idea中执行testng测试用例会报"Parameter 'envName' is required"

从Default菜单下选择TestNG

在如上位置加入-DenvName=local

1.2 将test-sdk项目导入eclipse或者idea,以eclipse为例进行导入

导入test-sdk项目示例如上图所示。

1.3 执行测试用例

示例中的测试用例代码是AppVersionAppClientTest,AppVersionAppClientTest中init是初始化函数,loadParam是获取参数函数,check和latests分别是对应2个接口的测试用例函数。

选中latests方法,右键弹出菜单Run As级联弹出菜单TestNG Test,执行TestNG Test,从Console窗口观察测试用例执行情况。

console输出如下

2 支持@DataProvider参数注入的方法,单元测试用例代码和测试数据分离

AppVersionAppClientTest代码如下

public class AppVersionAppClientTest {
    final Logger log = LogManager.getLogger(this.getClass());
    AppVersionAppClient appVersionAppClient = new AppVersionAppClient();
    Properties propParams = new Properties();
    @BeforeTest
    @org.testng.annotations.Parameters({
        "envName"
    })
    public void init(String envName) {
        appVersionAppClient.initSDKConfig("test-sdk");
        try {
            InputStream inParam = AppVersionAppClient.class.getResourceAsStream((("/testdata/"+ envName)+".properties"));
            propParams.load(inParam);
        } catch (IOException _x) {
            _x.printStackTrace();
        }
    }
    @DataProvider(name = "loadParam")
    public Object[][] loadParam(Method method) {
        Set<String> keys = propParams.stringPropertyNames();
        List<String[]> parameters = new ArrayList<String[]>();
        for (String key: keys) {
            if (key.startsWith((("AppVersionAppClient"+".")+(method.getName()+".")))) {
                String value = propParams.getProperty(key);
                parameters.add(new String[] {value });
            }
        }
        Object[][] object = new Object[parameters.size()][] ;
        for (int i = 0; (i<object.length); i ++) {
            String[] str = parameters.get(i);
            object[i] = new String[str.length] ;
            for (int j = 0; (j<str.length); j ++) {
                object[i][j] = str[j];
            }
        }
        return object;
    }
    @org.testng.annotations.Test(dataProvider = "loadParam")
    @org.testng.annotations.Parameters({
        "reqParamsJsonStr"
    })
    public void check(String reqParamsJsonStr) {
        JSONObject reqParamsJson = JSONObject.fromObject(reqParamsJsonStr);
        String appKey = reqParamsJson.optString("appKey");
        String appVersion = reqParamsJson.optString("appVersion");
        String appSource = reqParamsJson.optString("appSource");
        String deviceId = reqParamsJson.optString("deviceId");
        try {
            appVersionAppClient.check(appKey, appVersion, appSource, deviceId);
        } catch (Exception _x) {
            _x.printStackTrace();
        }
    }
    @org.testng.annotations.Test(dataProvider = "loadParam")
    @org.testng.annotations.Parameters({
        "reqParamsJsonStr"
    })
    public void latests(String reqParamsJsonStr) {
        JSONObject reqParamsJson = JSONObject.fromObject(reqParamsJsonStr);
        String appKey = reqParamsJson.optString("appKey");
        String appVersion = reqParamsJson.optString("appVersion");
        String appSource = reqParamsJson.optString("appSource");
        String deviceId = reqParamsJson.optString("deviceId");
        try {
            VersionListResponseBody responseBody = appVersionAppClient.latests(appKey, appVersion, appSource, deviceId);
            log.info(responseBody);
        } catch (Exception _x) {
            _x.printStackTrace();
        }
    }
}

org.testng.annotations.Parameters的参数envName既可以通过jvm -D的形式赋值,也可以通过pom中自定义属性赋值,在IDE中通过给所有testng的Run/Debug增加-DenvName=local的方式统一赋值,这样就可以直接执行单元测试用例了。init方法会在执行任意一个测试用例之前先执行,读取类路径/testdata/local.properties文件中的测试数据。这个文件在src/main/resources/testdata下,内容如下

该文件中的内容即是test-sdk用到的所有测试数据,被标注的即AppVersionAppClientTest需要用到的测试数据,该测试数据是通过testng的@DataProvider标签读入的,具体代码参见AppVersionAppClientTest的loadParam方法。

我们随机调整下local.properties中的测试数据如下

重新在eclipse中执行latests方法测试,console输出如下(可以看到请求数据和测试数据保持一致):

3.支持同一个单元测试用例执行多套测试数据

我们继续调整local.properties中的测试数据如下

对latests增加了一条测试数据,然后重新在eclipse中执行latests方法测试,console输出如下(可以看到输出了这两条测试数据都参与了测试,输出两条测试结果):

可以按照AppVersionAppClient.latests.n={}的格式继续增加测试数据,读者可自行实验,观察结果。

4.支持命令行执行,与maven-surefire-plugin无缝集成

参见本手册(一)-牛刀小试

5.支持通过配置文件testng.xml,配置需要测试的class、method

test-sdk工程的src/test/resources/testng.xml默认内容如下,默认配置了6个需要测试的class,执行效果参见本手册(一)-牛刀小试

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="clientTest" parallel="classes" thread-count="5"
	data-provider-thread-count="20">
	<test name="${envName}" parallel="methods"
		thread-count="5">
		<parameter name="envName" value="${envName}"></parameter>
		<classes>
			<class
				name="com.alibaba.testsdk.client.AppVersionGenericClientTest" />
			<class
				name="com.alibaba.testsdk.client.UserGenericClientTest" />
			<class name="com.alibaba.testsdk.client.UserAppClientTest" />
			<class
				name="com.alibaba.testsdk.client.AppVersionAppClientTest" />
			<class
				name="com.alibaba.testsdk.client.ArticleApiClientTest" />
			<class name="com.alibaba.testsdk.client.UserApiClientTest" />
		</classes>
	</test>
</suite>
5.1 下面我们过滤掉不需要测试的class
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd" >
<suite name="clientTest" parallel="classes" thread-count="5"
	data-provider-thread-count="20">
	<test name="${envName}" parallel="methods"
		thread-count="5">
		<parameter name="envName" value="${envName}"></parameter>
		<classes>
			<class name="com.alibaba.testsdk.client.AppVersionAppClientTest" />
		</classes>
	</test>
</suite>

执行测试并输出如下,可以看到只执行了AppVersionAppClientTest的2个测试用例代码,共测试了3条数据

mvn clean test -DskipTests=false
# 此处省略很长的一段输出
......
......
......
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.233 sec
Results :
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 6.089 s
[INFO] Finished at: 2018-05-29T18:37:07+08:00
[INFO] Final Memory: 15M/213M
[INFO] ------------------------------------------------------------------------
5.2 下面我们过滤掉不需要测试的method

执行测试并输出如下,可以看到只执行了AppVersionAppClientTest的1个测试用例代码,共测试了1条数据

mvn clean test -DskipTests=false
# 此处省略很长的一段输出
......
......
......
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.118 sec
Results :
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0
[INFO]
[INFO] --- maven-jar-plugin:2.4:jar (default-jar) @ test-sdk ---
[INFO] Building jar: C:\workshop\temp\test-sdk\target\test-sdk-1.1-SNAPSHOT.jar
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 9.004 s
[INFO] Finished at: 2018-05-29T18:47:13+08:00
[INFO] Final Memory: 23M/215M
[INFO] ------------------------------------------------------------------------

6.自带多线程,支持轻量的性能测试,有助于在开发联调阶段发现问题

testng既支持类级别的多线程并行,也支持方法级别的多线程并行,参见testng.xml配置文件如下

7.结合maven的profile使用testng的Parameters可以轻松实现test-sdk对接多套环境,执行自动化测试

7.1 在pom.xml中按照名称为local的profile节点再增加一个profile对应新的测试环境,节点名称假设为test

7.2 在src/test/resources/testdata目录下,新增test.properties,test.properties中按照local.properties灌入测试用例相关参数

7.3 命令行执行

#id为local和nocheck的profile的activeByDefault都被定义为true,默认是按照local和nocheck定义的属性来执行的,-P可以重新设置profile
mvn clean test -Ptest

7.4 观察控制台输出,可以看到当前是按照新的profile定义来执行测试用例了

7.5 当前目录下的target/surefire-reports下会生成最新测试的测试报告,可以通过用浏览器打开target/surefire-reports/index.html进行查看

⚠️ **GitHub.com Fallback** ⚠️