手把手教你接口自动化测试 – SoapUI & Groovy

时间:2021-11-19 03:54:12

手把手教你接口自动化测试 – SoapUI & Groovy

http://www.cnblogs.com/wade-xu/p/4236295.html 

 

关键词:SoapUI接口测试,接口自动化测试,数据驱动测试,SoapUI进阶使用, Groovy in SoapUI, SoapUI中Groovy的使用,数据分离。

 

阅读这篇文章需要一定的SoapUI基础,至少入过门,另外还需要一些Groovy的知识,当然如果你会java 也可以,这里用到的Groovy知识和Java很类似。

另外,本文的思路和我上一篇文章<零成本实现接口自动化测试 – Java+TestNG 测试Restful service>很相似,只不过把Java+TestNG的组合换成了SoapUI+Groovy, 另外测试对象也换成了基于Soap的web service, 依旧用Excel来管理数据,做到数据分离。

由于我用到的SoapUI是免费版本,相比Pro版,少很多的功能,像DataLoop之类的,所以只能通过Groovy写一些脚本来做数据驱动的测试。

 

首先打开SoapUI, 新建一个Workspace 名为Demo

手把手教你接口自动化测试 – SoapUI & Groovy

 

 

然后点击File->New soapUI Project

手把手教你接口自动化测试 – SoapUI & Groovy

 

然后输入你的Project Name, WSDL 地址 点击OK

 

手把手教你接口自动化测试 – SoapUI & Groovy

 

输入服务的用户名密码 点击OK

手把手教你接口自动化测试 – SoapUI & Groovy

 

项目工程建好了

手把手教你接口自动化测试 – SoapUI & Groovy

 

右击ServicePort 建立TestSuite

手把手教你接口自动化测试 – SoapUI & Groovy

 

以其中一个接口为例 来生成用例

手把手教你接口自动化测试 – SoapUI & Groovy

 

输入TestSuite name 然后确认

工程结构如下

手把手教你接口自动化测试 – SoapUI & Groovy

 

 

通过右击TestCase -> Add Step  增加 Groovy Script 和 Properties

手把手教你接口自动化测试 – SoapUI & Groovy

 

增加四个Groovy Script, 并且命名成 Start, Process, Check Response, End

增加5个 Properties, 并且命名成Input, Baseline, Output, Result, fieldResult

调整它们的顺序,最后形成下图的工程目录结构

手把手教你接口自动化测试 – SoapUI & Groovy

 

 

Start脚本主要创建log文件

手把手教你接口自动化测试 – SoapUI & Groovy手把手教你接口自动化测试 – SoapUI & Groovy
/*Check the log file, create folder and file if necessary*/
import java.io.*
;

def cal
= Calendar.instance;
def sysdate = cal.getTime();
def Y = cal.get(Calendar.YEAR);
def M = cal.get(Calendar.MONTH)+1;
def D = cal.get(Calendar.DATE);
if (D<10) D = "0"+D;
if (M<10) M = "0"+M;
date = Y+"-"+M+"-"+D;
time=sysdate.toString().replaceAll(':','-')

def testSuites
=testRunner.testCase.getTestSuite();
def testcase = testRunner.testCase;

def logFolder
= new File(context.expand('${#Project#LogFolder}'));
def responseLogName = (context.expand('${#Project#LogFolder}')+date+'\\'+testSuites.name+' - '+ testcase.name +' - ' + time+".log");
def responseDetailLogName = (context.expand('${#Project#LogFolder}')+date+'\\'+testSuites.name+' - ' + testcase.name +' - ' + time + " Response Detail.log");
def responseLogFile = new File(responseLogName);
def responseDetailLogFile = new File(responseDetailLogName);
def subFolder= new File (context.expand('${#Project#LogFolder}')+date+'\\')

/*Set date and Log Name to Project - Testcase Properties*/
testcase.setPropertyValue('date'
, date);
testcase.setPropertyValue('LogFile - Check Response', responseLogName);
testcase.setPropertyValue('LogFile - Response Detail', responseDetailLogName);

if(!logFolder.exists()){
logFolder.mkdirs()
;
}

if(!subFolder.exists()){
subFolder.mkdirs()
;
}

/*Check the file
, create a new one if not found*/
if(!responseLogFile.exists()){
responseLogFile.createNewFile()
;
}
responseLogFile.append(
"---------------Test Start on "+sysdate+" ------------------------"+'\n');

if(!responseDetailLogFile.exists()){
responseDetailLogFile.createNewFile()
;
}
responseDetailLogFile.append(
"---------------Test Start on "+sysdate+" ------------------------"+'\n');
View Script - Start

 

Process脚本是整个工程的核心,读取Excel数据文件的Input, Baseline 放入二维数组,然后循环读入Input和Baseline这两个Properties, 调用request, 取到Ouput和Result Properties的值放入Output, Result数组,最后更新Excel的Output, Result, Comparison sheet。

手把手教你接口自动化测试 – SoapUI & Groovy手把手教你接口自动化测试 – SoapUI & Groovy
import java.io.*;
import jxl.*;
import java.text.DecimalFormat;

def readInput(workbook
,inputSheetName)
{
Sheet sheet
=workbook.getSheet(inputSheetName);

rows
= sheet.getRows();
columns = sheet.getColumns();

/*Get content into array*/
input
= new Object [columns][rows]
for (a
=0;a<columns;a++)
{
for (b
=0;b<rows;b++)
{
input
[a][b]= sheet.getCell(a,b).getContents();
}
}
}

def readBaseline(workbook
,sheetName)
{
Sheet sheet
=workbook.getSheet(sheetName);

Rows
= sheet.getRows();
Columns = sheet.getColumns();

/*Get content into array*/
baseline
= new Object [Columns][Rows]
for (a
=0;a<Columns;a++)
{
for (b
=0;b<Rows;b++)
{
baseline
[a][b]= sheet.getCell(a,b).getContents();
}
}
}

def updateOutput(writableWorkbook
,sheetName,start_row,rows,columnNo,result,resultTag){

Sheet
[] sheet= writableWorkbook.getSheets();
ws = writableWorkbook.getSheet(sheetName);
if(ws==null){
ws
= writableWorkbook.createSheet(sheetName,sheet.length)
}
for (a
=0;a<columnNo;a++)
{
ws.addCell(new jxl.write.Label(a
,0,result[a][0]));
for (b=start_row;b<rows;b++)
{
wc
= new jxl.write.WritableCellFormat();
if (resultTag[a][b]=='PASS')
{
wc.setBackground(jxl.format.Colour.WHITE)
;
ws.addCell(new jxl.write.Label(a,b,result[a][b],wc));
}
else if(resultTag
[a][b]=='FAIL'){
wc.setBackground(jxl.format.Colour.RED)
;
ws.addCell(new jxl.write.Label(a,b,result[a][b],wc));
}
}
}
}

def updateResult(writableWorkbook
,sheetName,start_row,rows,columnNo,result){

Sheet
[] sheet= writableWorkbook.getSheets();
ws = writableWorkbook.getSheet(sheetName);
if(ws==null){
ws
= writableWorkbook.createSheet(sheetName,sheet.length)
}
for (a
=0;a<columnNo;a++)
{
ws.addCell(new jxl.write.Label(a
,0,result[a][0]));
for (b=start_row;b<rows;b++)
{
wcf
= new jxl.write.WritableCellFormat();
wcf.setBackground(jxl.format.Colour.RED);

if(result
[a][b] == 'FAIL'){
ws.addCell(new jxl.write.Label(a
,b,result[a][b],wcf));
} else
ws.addCell(new jxl.write.Label(a
,b,result[a][b]));
}
}

wc
= new jxl.write.WritableCellFormat();
wcc = new jxl.write.WritableCellFormat();
wc.setBackground(jxl.format.Colour.GRAY_25);
wcc.setBackground(jxl.format.Colour.LIGHT_TURQUOISE);
ws.addCell(new jxl.write.Label(0,rows-3,result[0][rows-3],wcc));
ws.addCell(new jxl.write.Label(0,rows-2,result[0][rows-2],wc));
ws.addCell(new jxl.write.Label(0,rows-1,result[0][rows-1],wc));

}

def updateComparison(writableWorkbook
,sheetName,start_row,rows,columnNo,output,outputTag,result,baseline){

Sheet
[] sheet= writableWorkbook.getSheets();
ws = writableWorkbook.getSheet(sheetName);
if(ws==null){
ws
= writableWorkbook.createSheet(sheetName,sheet.length)
}
for (a
=0;a<columnNo;a++)
{
ws.addCell(new jxl.write.Label(a
,0,output[a][0]));

x
= 1;
for (b=start_row;b<rows;b++)
{
if(result
[1][b] == 'FAIL'){
wc
= new jxl.write.WritableCellFormat();
if (outputTag[a][b]=='PASS')
{
wc.setBackground(jxl.format.Colour.WHITE)
;
ws.addCell(new jxl.write.Label(a,x,output[a][b],wc));
}
else {
wc.setBackground(jxl.format.Colour.RED)
;
ws.addCell(new jxl.write.Label(a,x,output[a][b],wc));
}
wbc
= new jxl.write.WritableCellFormat();
wbc.setBackground(jxl.format.Colour.ICE_BLUE);
ws.addCell(new jxl.write.Label(a,x+1,baseline[a][b],wbc));
x+=2;
}
}
}
}

def removeSheetByName(writableWorkbook
, name){

Sheet
[] sheet= writableWorkbook.getSheets();
ws = writableWorkbook.getSheet(name);
if(ws != null){
for (i
= 0; i < sheet.length; i++) {
sheetName = writableWorkbook.getSheet(i).getName();
if (sheetName.equalsIgnoreCase(name)) {
writableWorkbook.removeSheet(i)
;
}
}
}
}

def setProperties(Name
,Value,Place)
{
name
= Name;
target = testRunner.testCase.getTestStepByName(Place);
target.setPropertyValue(name,Value);
}

def cleanProperty(PropertyListName)
{
PropertyList
= testRunner.testCase.getTestStepByName(PropertyListName);
size=PropertyList.getPropertyCount();
if (size!=0)
{
for (i
=0;i<size;i++)
{
PropertyList.removeProperty(PropertyList.getPropertyAt(
0).name);
}
}
}

def logFile
= new File(context.expand('${#TestCase#LogFile - Check Response}'));
def xlsName = context.expand('${#TestCase#Workbook}');

def project
= testRunner.testCase.getTestSuite().getProject();
def testSuite = testRunner.testCase.getTestSuite();
def testcase = testRunner.testCase
def requests
= testcase.getTestStepsOfType( com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep.class )
def request
= requests[0];

inputSheetName
= "Input";
baselineSheet = "Baseline";
outputSheet = "Output";
resultSheet = "Result";
fieldResult = testcase.getTestStepByName('fieldResult');
ComparisonSheet = "Comparison";

Baseline
= testcase.getTestStepByName(baselineSheet);
baselineSize = Baseline.getPropertyCount();
Output = testcase.getTestStepByName(outputSheet);

def cal
= Calendar.instance;
def sysdate = cal.getTime();
sleepTime=context.expand('${#Project#sleepTime}').toInteger()

try{
Workbook workbook
=Workbook.getWorkbook(new File(xlsName));
cleanProperty(inputSheetName);
readInput(workbook,inputSheetName);

for (i
=0;i<columns;i++)
{
setProperties(input
[i][0],'',inputSheetName)
}

cleanProperty(baselineSheet)
;
readBaseline(workbook,baselineSheet);
for (i=0;i<Columns;i++)
{
setProperties(baseline
[i][0],'',baselineSheet)
}
workbook.close()
;

}catch(Exception e){
e.printStackTrace()
;
}

/*-----------Set runSelect as 'true' in Project property to run selected test cases from startTag till endTag-------*/
def runSelected
= context.expand('${#Project#runSelected}')
start_Test
=1;
end_Test=rows-1;

def passNumbers
= 0;
def decFormat = new DecimalFormat("##.00%");


if ('true'.equalsIgnoreCase(runSelected))
{
startTag
=context.expand('${#Project#startTag}').toInteger();
endTag=context.expand('${#Project#endTag}').toInteger();

if((
0<startTag)&&(startTag<rows)&&(endTag>=startTag)&&(rows>endTag)){
start_Test
=startTag;
end_Test=endTag;
}
}

result
= new Object [2][rows+3]
result
[0][0]='Case Description';
result[1][0]='Result'
result
[0][rows+1]='Start Time:';
result[1][rows+1]=sysdate.toString();

/*--New object and put the output name and value into this list--*/
output
= new Object [baselineSize][rows]
outputTag
= new Object [baselineSize][rows]
for (i
=0;i<baselineSize;i++)
{
output
[i][0]= Baseline.getPropertyAt(i).name;
outputTag[i][0]= 'PASS';
}

for (m
=start_Test;m<=end_Test;m++)
{
logFile.append('\n'+ testcase.name +
": "+m+" "+ ". "+sysdate+'\n');
for (i=0;i<columns;i++)
{
setProperties(input
[i][0],input[i][m],inputSheetName)
}
for (j
=0;j<Columns;j++)
{
setProperties(baseline
[j][0],baseline[j][m],baselineSheet)
}

testRunner.runTestStepByName(request.name)
;
Thread.sleep(sleepTime);
testRunner.runTestStepByName("Check Response");

result
[0][m]=context.expand('${'+inputSheetName+'#Case Description}')
result
[1][m]=context.expand('${'+resultSheet+'#result}')

if(result
[1][m] == 'PASS'){
passNumbers++
;
}

for (i
=0;i<baselineSize;i++)
{
output
[i][m]= Output.getPropertyAt(i).value;
outputTag[i][m]= fieldResult.getPropertyAt(i).value;
}
}
result
[0][rows+2]='End Time:';
result[1][rows+2]=sysdate.toString();
result[0][rows]='Pass Percentage:';

passPercentage
= decFormat.format(passNumbers/(end_Test-start_Test+1));
result[1][rows] = passPercentage

/*--------------Update Output
, Result, Comparison sheet---------*/
try{
workbook
= Workbook.getWorkbook(new File(xlsName));
writableWorkbook = Workbook.createWorkbook(new File(xlsName), workbook);

updateOutput(writableWorkbook
,outputSheet,start_Test,end_Test+1,baselineSize,output,outputTag);
updateResult(writableWorkbook,resultSheet,start_Test,rows+3,2,result);

removeSheetByName(writableWorkbook
,ComparisonSheet);

if(passPercentage !
= '100.00%'){
updateComparison(writableWorkbook
,ComparisonSheet,start_Test,end_Test+1,baselineSize,output,outputTag,result,baseline);
}

writableWorkbook.write()
;
writableWorkbook.close();
workbook.close();

}catch(Exception e){
e.printStackTrace()
;
}

setProperties('passPercentage'
, passPercentage ,'Result');

testRunner.gotoStepByName('End')
;
View Script - Process

 

Check Response 顾名思义,用来检查返回结果, 通过XmlHolder getNodeValue 来取response各节点的值,并且填入Output Properties已作对比之用。

手把手教你接口自动化测试 – SoapUI & Groovy手把手教你接口自动化测试 – SoapUI & Groovy
import java.lang.*;
import java.util.*;
import groovy.lang.*;
import groovy.util.*;
import com.eviware.soapui.support.XmlHolder

baselineSheet
= "Baseline";
outputSheet = "Output";
resultSheet = "Result";

def testcase
= testRunner.testCase;
Baseline = testcase.getTestStepByName(baselineSheet)
baselineSize
= Baseline.getPropertyCount();
Output = testcase.getTestStepByName(outputSheet);

def requests
= testcase.getTestStepsOfType( com.eviware.soapui.impl.wsdl.teststeps.WsdlTestRequestStep.class )
def request
= requests[0];
def response = request.testRequest.response;

respXmlHolder
= new XmlHolder(response.contentAsXml)

//declare namespace in xml response
respXmlHolder.declareNamespace(
"ns1", "http://schemas.xxx.com/v201203/yourservice")

def statusCode
= request.testRequest.response.responseHeaders["#status#"].toString();

def setProperties(Name
,Value,Place){
name
= Name;
target = testRunner.testCase.getTestStepByName(Place);
target.setPropertyValue(name,Value);
}

def getspecifiedValue(field){
prefix
= "//ns1:";
nodePath = "${prefix}${field}"
specifiedValue
= respXmlHolder.getNodeValue("${nodePath}")
}

testRunner.testCase.getTestStepByName(outputSheet).clearPropertyValues()
;

//normal output
, status = '200 OK'
if(statusCode.contains('
200 OK')){
for (i
=1;i<baselineSize;i++)
{
specifiedName
= Baseline.getPropertyAt(i).name;
specifiedValue = getspecifiedValue(specifiedName);
if(specifiedValue != null){
setProperties(specifiedName
,specifiedValue,outputSheet)
} else {
setProperties(specifiedName
,'',outputSheet)
}
}

} else
{
setProperties(Baseline.getPropertyAt(
0).name,Baseline.getPropertyAt(0).value,outputSheet);
setProperties(Baseline.getPropertyAt(1).name,statusCode,outputSheet);

for(t
=2; t<baselineSize; t++){
setProperties(Baseline.getPropertyAt(t).name, '' ,outputSheet);
}
}

setProperties(Baseline.getPropertyAt(
0).name,Baseline.getPropertyAt(0).value,outputSheet);
setProperties('result','PASS', resultSheet);


logFile
= new File(context.expand('${#TestCase#LogFile - Check Response}'));
responseDetailLogFile= new File(context.expand('${#TestCase#LogFile - Response Detail}'));

logFile.append(
" ------ Start Response Check "+ " @ "+Calendar.instance.getTime()+"\n");
responseDetailLogFile.append("\n"+ testcase.name + " -- " + Baseline.getPropertyAt(0).value+ "\n" + response+"\n");


/*--------Compare the result with Baseline and update the result accordingly---------*/

for (i
=0;i<baselineSize;i++)
{
if (Baseline.getPropertyAt(i).value
==Output.getPropertyAt(i).value)
{
setProperties(Baseline.getPropertyAt(i).name
,'PASS','fieldResult');
}
else
{
setProperties(Baseline.getPropertyAt(i).name
,'FAIL','fieldResult');
setProperties('result','FAIL','Result');
}
}
View Script - Check Response

 

End脚本 在log上打时间戳

手把手教你接口自动化测试 – SoapUI & Groovy手把手教你接口自动化测试 – SoapUI & Groovy
def cal = Calendar.instance;
def sysdate = cal.getTime();

responseLogFile
= new File(context.expand('${#TestCase#LogFile - Check Response}'));
responseDetailLogFile= new File(context.expand('${#TestCase#LogFile - Response Detail}'));

responseLogFile.append('\n'+
"---------------Test End on " + sysdate.toString() + " ------------------------"+'\n');
responseDetailLogFile.append('\n'+ "---------------Test End on " + sysdate.toString() + " ------------------------"+'\n');
View Script - End

 

配置

双击DemoProject, 点击下方的Properties tab

然后 Add property

手把手教你接口自动化测试 – SoapUI & Groovy

手把手教你接口自动化测试 – SoapUI & Groovy

Add 如下图所示的5个Property

手把手教你接口自动化测试 – SoapUI & Groovy

 

双击项目工程列表里的'Demo TestCase'

点击Properties tab

Add Property 如图

手把手教你接口自动化测试 – SoapUI & Groovy

 

Value 填Excel 的路径

手把手教你接口自动化测试 – SoapUI & Groovy

 

Excel数据工作簿里的Input sheet 如图

手把手教你接口自动化测试 – SoapUI & Groovy

 

接下来需要把Input里的column name 与 Soap request里的input 字段映射起来

双击Test Steps 里的request

将xml文件里的?用参数来代替

手把手教你接口自动化测试 – SoapUI & Groovy

左下角的TestRequest Properties  要填上用户名 密码

手把手教你接口自动化测试 – SoapUI & Groovy

 

Baseline sheet里要把输出结果的字段名都定义好, 因为是根据字段名去response里取结果的

每条用例期望结果都写好,用作和实际结果对比

手把手教你接口自动化测试 – SoapUI & Groovy

 

另外Check Response里的脚本需要设置一下

假设你的response xml文件结构如下

手把手教你接口自动化测试 – SoapUI & Groovy

如果你需要检查Soap body标签下的内容, 则你需要配置一下Check Response的脚本

将xml namespace的路径配置一下

手把手教你接口自动化测试 – SoapUI & Groovy

 

右击Project  Save一下Project 

最后可以运行了 双击Demo TestSuite  点击Run

手把手教你接口自动化测试 – SoapUI & Groovy

注意:Groovy脚本也是用第三方的jxl.jar操作excel文件的,所以这个jar需要放到SoapUI安装目录的\lib下面,才能运行成功。

 

打开Workbook 数据工作簿查看结果

Output

手把手教你接口自动化测试 – SoapUI & Groovy

Result

手把手教你接口自动化测试 – SoapUI & Groovy

Comparison

手把手教你接口自动化测试 – SoapUI & Groovy

 

是不是跟我上一篇文章异曲同工?

 

 

注:转载需注明出处及作者名,严禁恶意转载,尊重原作者的劳动成果。