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

简介: 原文:手把手教你接口自动化测试 – SoapUI & Groovy手把手教你接口自动化测试 – SoapUI & Groovy   关键词:SoapUI接口测试,接口自动化测试,数据驱动测试,SoapUI进阶使用, Groovy in SoapUI, SoapUI中Groovy的使用,数据分离。
原文: 手把手教你接口自动化测试 – SoapUI & Groovy

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

 

关键词: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

 

 

然后点击File->New soapUI Project

 

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

 

 

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

 

项目工程建好了

 

右击ServicePort 建立TestSuite

 

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

 

输入TestSuite name 然后确认

工程结构如下

 

 

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

 

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

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

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

 

 

Start脚本主要创建log文件

/*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。

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已作对比之用。

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上打时间戳

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

Add 如下图所示的5个Property

 

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

点击Properties tab

Add Property 如图

 

Value 填Excel 的路径

 

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

 

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

双击Test Steps 里的request

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

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

 

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

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

 

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

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

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

将xml namespace的路径配置一下

 

右击Project  Save一下Project 

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

 

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

Output

Result

Comparison

 

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

 

 

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

 

目录
相关文章
|
12月前
|
设计模式 前端开发 测试技术
告别脆弱:构建稳定UI自动化测试的3个核心策略
告别脆弱:构建稳定UI自动化测试的3个核心策略
977 113
|
存储 关系型数据库 测试技术
玩转n8n测试自动化:核心节点详解与测试实战指南
n8n中节点是自动化测试的核心,涵盖触发器、数据操作、逻辑控制和工具节点。通过组合节点,测试工程师可构建高效、智能的测试流程,提升测试自动化能力。
|
XML jenkins 机器人
JMeter+Ant+Jenkins实现接口自动化测试持续集成
本文介绍了如何使用Ant生成JMeter接口测试报告,并集成到Jenkins中实现自动化测试。内容涵盖Ant与JMeter环境配置、build.xml文件设置、测试执行及报告生成,同时包括Jenkins插件安装、项目配置和钉钉消息通知的集成,帮助实现持续测试与结果可视化。
1462 0
|
机器学习/深度学习 人工智能 测试技术
EdgeMark:嵌入式人工智能工具的自动化与基准测试系统——论文阅读
EdgeMark是一个面向嵌入式AI的自动化部署与基准测试系统,支持TensorFlow Lite Micro、Edge Impulse等主流工具,通过模块化架构实现模型生成、优化、转换与部署全流程自动化,并提供跨平台性能对比,助力开发者在资源受限设备上高效选择与部署AI模型。
987 9
EdgeMark:嵌入式人工智能工具的自动化与基准测试系统——论文阅读
|
人工智能 前端开发 测试技术
如何让AI帮你做前端自动化测试?我们这样落地了
本文介绍了一个基于AI的UI自动化测试框架在专有云质量保障中的工程化实践。
5012 24
如何让AI帮你做前端自动化测试?我们这样落地了
|
自然语言处理 前端开发 测试技术
使用 Playwright MCP 实现 UI 自动化测试
本文介绍如何结合Playwright与MCP协议实现智能化UI自动化测试。通过自然语言指令控制浏览器,降低技术门槛,提升效率,并涵盖环境搭建、核心功能、实战案例及最佳实践,展现对话式自动化的未来趋势。
|
Web App开发 开发框架 .NET
Playwright 自动化测试系列(6)| 第三阶段:测试框架集成​指南:参数化测试 + 多浏览器并行执行
Pytest 与 Playwright 集成可提升自动化测试效率,支持参数化测试、多浏览器并行执行及统一报告生成。通过数据驱动、Fixture 管理和并行优化,显著增强测试覆盖率与执行速度,适用于复杂 Web 应用测试场景。
|
Java 测试技术 容器
Jmeter工具使用:HTTP接口性能测试实战
希望这篇文章能够帮助你初步理解如何使用JMeter进行HTTP接口性能测试,有兴趣的话,你可以研究更多关于JMeter的内容。记住,只有理解并掌握了这些工具,你才能充分利用它们发挥其应有的价值。+
1789 23
|
数据采集 JSON 前端开发
GraphQL接口采集:自动化发现和提取隐藏数据字段
本文围绕GraphQL接口采集展开,详解如何通过`requests`+`Session`自动化提取隐藏数据字段,结合爬虫代理、Cookie与User-Agent设置实现精准抓取。内容涵盖错误示例(传统HTML解析弊端)、正确姿势(GraphQL请求构造)、原因解释(效率优势)、陷阱提示(反爬机制)及模板推荐(可复用代码)。掌握全文技巧,助你高效采集Yelp商家信息,避免常见误区,快速上手中高级爬虫开发。
405 3
GraphQL接口采集:自动化发现和提取隐藏数据字段

热门文章

最新文章