目录
1 概述
1.1 概述
“工欲善其事,必先利其器”,如果有好的工具而我们不知道如何使用,那就太可惜了。前期用SoapUI做接口测试,顺便接触了一下其提供的自动化能力,感觉还是非常强大的。现就使用SoapUI做自动化测试的一些经验进行总结共享,以期对读者有所帮助。需要说明的是,作者知识有限,对此研究也没有十分深入,所以本文主要是给初学者一些引导。
另外,本文要求读者具备一定的SoapUI基本知识、自动化基础知识以及编程基础(这里的编程主要是groovy编程)。我会在第二章中介绍最重要的几点预备知识,读者也可以参考如下链接进行自学:
SoapUI基础:https://www.soapui.org/soapui-projects/soapui-projects.html
Smart bear论坛(可以在此搜索SoapUI问题):https://community.smartbear.com/
SoapUI API: http://www.soapui.org/apidocs/index.html
Groovy基础:https://www.w3cschool.cn/groovy/groovy_overview.html
Groovy Tutorial:https://www.tutorialspoint.com/groovy/index.htm
2 预备知识
2.1 SoapUI工程结构
这里只介绍SoapUI关于功能测试的结构,主要分三个层级:TestSuites, TestCases 和TestSteps。TestSuites创建于工程(project)目录之下。三者定义如下:
SoapUI structuresfunctional tests into three levels; TestSuites, TestCases and TestSteps.
- A TestSuite is a collection of TestCases that can be used for grouping functional tests into logical units. Any number of TestSuites can be created inside a soapUI project to support massive testing scenarios.
- A TestCase is a collection of TestSteps that are assembled to test some specific aspect of your service(s). You can add any number of TestCases to a containing TestSuite and even modularize them to call each other for complex testing scenarios.
- TestSteps are the "building blocks" of functional tests in soapUI. They are added to a TestCase and used control the flow of execution and validate the functionality of the service(s) to be tested.
参考链接:https://www.soapui.org/docs/functional-testing/structuring-and-running-tests.html
2.2 自动化测试基本概念
所谓自动化测试,就是把以人为驱动的测试行为转化为机器执行。在软件测试的过程中,我们通常是先获取数据,然后执行步骤,最后验证执行结果。自动化测试也不例外,只是把这些步骤统统交给自动化脚本去做,从而让测试用例可以快速高效地执行,并且可以反复地执行。在第三章中,我也基本遵循该顺序来介绍用SoapUI实现自动化测试。
2.3 Groovy基础
2.3.1 读写文件
可以用如下语句读取文件的每一行并打印,方法eachLine内置在Groovy中的File类中,目的是确保文本文件的每一行都被读取。
new File("E:/Example.txt").eachLine {
line-> log.info "line : $line";
}
可以用如下语句将内容写入文件:
File file = new File("E:/Example.txt");
file.append("Hello World!");
2.3.2 读取数据库
可以用下面的语句来查询数据库记录:
import groovy.sql.Sql
import java.sql.Connection;
// 新建数据库连接
def connector = Sql.newInstance(
"jdbc:oracle:thin:@ localhost:1526:testdb",
"username",
"password",
"oracle.jdbc.driver.OracleDriver");
// 执行查询
String sql = "select sysdate from dual";
connector.eachRow(sql){
row ->
log.inforow.toString();
}
// 关闭连接
connector.close();
2.3.3 JSON处理
因为有不少的接口请求是JSON格式的,所以将用groovy处理JSON文本单独做个介绍。JSON是一种基于键值对的数据交换语言,而通常获取到的SoapUI请求报文是String类型,因而并不具备键值对属性,所以首先需要将文本格式的JSON解析为groovy的数据结构。然后即可利用键值对中的“键”来读取和设置“值”。最后,需要将groovy类的数据结构再还原成JSON文本并设置到接口请求中。
例如已有名为“TEST”的TestSteps,其请求报文为如下JSON文本:
{
"name":"John",
"ID":1
}
则可以用如下的语句完成前述操作:
import groovy.json.JsonSlurper
import groovy.json.JsonBuilder
// 获取原始请求
def step = testRunner.testCase.testSteps["TEST"];
def request = step.testRequest.getRequestContent();
// 将文本JSON转换为groovy对象
def jsonRequest = new JsonSlurper().parseText(request);
// 设置请求值
jsonRequest.name = "Jack";
// 将修改之后的请求重新转换成JSON文本并设置为请求报文
def output = newJsonBuilder(jsonRequest).toPrettyString();
step.testRequest.setRequestContent(output);
需要说明的是,JSON中的键值对之间本身是无序的。Groovy对象针对这些无序的键值对自有其内部的排序机制,所以经过上述程序处理之后,你可能会看到键值对的顺序发生了改变,这是无可避免的,除非你自己编写一个JSON处理方法。
3 用SoapUI实现自动化测试
3.1 获取测试数据
测试数据通常有两类,一是环境信息有关的测试数据,另外一种就是与测试用例相关的业务数据。让测试数据和测试用例解耦可以提高用例的可移植性。测试数据的来源基本有这么几种:文件、数据库以及其他用例或步骤的执行结果等。以其他用例或步骤的执行结果为测试数据来源的获取请参考下文校验执行结果,因为既然要校验执行结果,肯定要先获取执行结果。这里只概要介绍从文件和从数据库获取测试数据
l 从文件获取
个人推荐将测试数据以键值对的形式存放在“.properties”文件中,这样从文件中读取时,就可以直接将测试数据读入到groovy的map对象,方便后续使用这些数据。“.properties”的内容示例如下:
Endpoint=http://10.57.79.91:18180/enabler/httpservices
serviceNumber=987650009
则可以通过如下代码将测试数据读取到自定义的properties中并使用:
def properties = new Properties()
newFile("E:/TestData.properties").withInputStream {
stream ->properties.load(stream)
}
// 使用读取到的测试数据
testStep.httpRequest.setEndpoint(properties["Endpoint"]);
l 从数据库中获取
从数据库中获取可以参考前述的读取数据库,如下代码做了一些优化:
com.eviware.soapui.support.GroovyUtils.registerJdbcDriver("oracle.jdbc.driver.OracleDriver");
def connector = Sql.newInstance(
properties["Connector"],
properties["User"],
properties["Password"],
properties["Driver"]);
String sqlString="selectt2.cust_id,t2.cust_code,t1.* from subs_user.inf_subscriber t1,cust_user.inf_customer t2 where t1.service_number=" +properties["serviceNumber"] + "andt2.cust_id=t1.owner_party_role_id";
connector.eachRow(sqlString) { row ->
log.inforow;
properties.put("customerId",String.valueOf(row['CUST_ID']));
}
connector.close();
3.2 获取请求报文并更新测试数据
可以用如下的语句来获取到project,testsuite,testcase和teststep对象:
def project = testRunner.testCase.testSuite.project;
def project = context.testCase.testSuite.project;
def myTestSuite = project.getTestSuiteAt(IndexNumber);
def myTestSuite = project.getTestSuiteByName(“Name ofthe TestSuite”);
def myTestCase = myTestSuite.getTestCaseAt(IndexNumber);
def myTestCase = myTestSuite.getTestCaseByName(“Name ofthe TestCase”);
def myTestStep = myTestCase.getTestStepAt(IndexNumber);
def myTestStep = myTestCase.getTestStepByName(“Name ofthe TestStep”);
有时候我们可能希望针对某个TestSuite统一设置其下所有testSteps的参数,这可以通过以下的代码来实现。假设已有名为“TestSuite - AllTestCases”的TestSuite:
// Set test data for each teststep
def text = "*";
def testSuite =project.getTestSuiteByName("TestSuite - AllTestCases");
log.info "${text*5} TestSuite :: $testSuite.name${text*5}";
def testCaseList = testSuite.getTestCases();
testCaseList.each
{
deftestCase = testSuite.getTestCaseByName(it.key);
log.info"${text*5} testCase :: $testCase.name ${text*5}";
deftestSteps = testCase.getTestStepList();
log.infotestSteps;
testSteps.each
{
testStep->
log.info"${text*5} teststep :: $testStep.name ${text*5} testtype ==$testStep.config.type";
if(testStep.config.type== "restrequest")
{
//Set endpoint for all test steps
testStep.httpRequest.setEndpoint(properties["Endpoint"]);
}
if(testStep.config.type != "groovy"){
defrequest = testStep.testRequest.getRequestContent();
log.info"request: $request"
defslurper = new JsonSlurper();
LinkedHashMapjsonRequest = slurper.parseText(request);
//Update parameters for request
if(null != jsonRequest.taskRequest){
jsonRequest.taskRequest.serviceNumber= properties["serviceNumber"];
}
//Set new request to rquest content
defoutput = new JsonBuilder(jsonRequest).toPrettyString();
log.info"new output: $output";
testStep.testRequest.setRequestContent(output);
}
}
}
3.3 设置断点,校验执行结果
测试最重要的是检查执行结果来判断系统的实现是否符合预期。SoapUI用例执行的结果检查通常是通过设置断点来实现的。关于断点设置的基础知识请参考:
目前SoapUI断点有两种:一种是在用例的TestSteps中设置断点,另一种是单独编写一个断点TestStep(该功能只在SoapUI PRO中支持)。本文仅介绍第一种断点设置方式。
例如,针对修改用户状态的用例,我设置了如下断点来校验用例执行之后的订单状态。可以参考这段代码来获取响应报文,查询数据库以及设置断点等。
@GrabConfig(systemClassLoader = true)
import groovy.xml.MarkupBuilder
import groovy.sql.Sql
import java.sql.Connection;
import groovy.json.JsonSlurper
import groovy.json.JsonOutput
import groovy.json.JsonBuilder
def response = messageExchange.response.responseContent;
def slurper = new JsonSlurper();
def jsonResponse = slurper.parseText(response);
// Get order id
log.info "$context.currentStep.name:$jsonResponse"
def orderId = jsonResponse.orderBasicInfo.id;
// get test data from file
def properties = new Properties()
newFile("E:/TestData.properties").withInputStream {
stream ->properties.load(stream)
}
// Check order status
com.eviware.soapui.support.GroovyUtils.registerJdbcDriver("oracle.jdbc.driver.OracleDriver");
def connector = Sql.newInstance(
properties["Connector"],
properties["User"],
properties["Password"],
properties["Driver"]);
String sqlOfOrder="select t.status,t.*,t.rowid fromorder_user.om_order t where t.order_id='$orderId'";
String sqlOfOrderHis="select t.status,t.*,t.rowidfrom order_user.om_order_his t where t.order_id='$orderId'";
def status = "";
def count = 0;
while (status != "OS90" && status !="OS50" && count < 10) {
def result= connector.firstRow(sqlOfOrderHis);
if (null !=result){
status= result.status;
log.info"current status: $status";
} else {
Thread.sleep(2000);
result= connector.firstRow(sqlOfOrder);
if(null != result){
status= result.status;
log.info"current status: $status";
}
}
count++;
}
connector.close();
assert status == "OS90";
设置断点还可以用来将当前TestStep的结果数据设置为其他TestStep的测试数据,具体的方法结合可以综合上述的结果获取和测试数据设置。当然,你也可以在其他TestStep执行之前获取某个TestStep的执行结果来设置测试数据。
3.4 批量执行用例
编写完所有的自动化用例,即可批量执行。批量执行的方式已知的有两种,一种是双击TestSuit之后,点击界面的绿色三角符号执行;另一种是右键单击TestSuit ->单击Launch TestRunner -> 单击Launch执行。第一种方式较简单,可以直观看到用例执行的结果,执行成功的会展示为绿色,执行失败的展示为红色。另外还可以通过绿色执行按钮旁边的菜单设置顺序执行和并发执行。第二种执行方式提供了更多的设置,包括设置endpoint,设置执行结果报告,设置变量属性等功能。
l 第一种批量执行方式:
l 第二种批量执行方式:
4 附件
4.1 TestSuit参考
我将已经实现的一个TestSuit添加于此,有需要的可以参考: