手把手教你接口自动化测试 – 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

 

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

 

 

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

 

目录
打赏
0
0
0
0
216
分享
相关文章
接口测试新选择:Postman替代方案全解析
在软件开发中,接口测试工具至关重要。Postman长期占据主导地位,但随着国产工具的崛起,越来越多开发者转向更适合中国市场的替代方案——Apifox。它不仅支持中英文切换、完全免费不限人数,还具备强大的可视化操作、自动生成文档和API调试功能,极大简化了开发流程。
除了postman还有什么接口测试工具
最好还是使用国内的接口测试软件,其实国内替换postman的软件有很多,这里我推荐使用yunedit-post这款接口测试工具来代替postman,因为它除了接口测试功能外,在动态参数的支持、后置处理执行sql语句等支持方面做得比较好。而且还有接口分享功能,可以生成接口文档给团队在线浏览。
55 2
大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡
大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡
121 10
大前端之前端开发接口测试工具postman的使用方法-简单get接口请求测试的使用方法-简单教学一看就会-以实际例子来说明-优雅草卓伊凡
以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
91 5
以项目登录接口为例-大前端之开发postman请求接口带token的请求测试-前端开发必学之一-如果要学会联调接口而不是纯写静态前端页面-这个是必学-本文以优雅草蜻蜓Q系统API为实践来演示我们如何带token请求接口-优雅草卓伊凡
Python测试淘宝店铺所有商品接口的详细指南
本文详细介绍如何使用Python测试淘宝店铺商品接口,涵盖环境搭建、API接入、签名生成、请求发送、数据解析与存储、异常处理等步骤。通过具体代码示例,帮助开发者轻松获取和分析淘宝店铺商品数据,适用于电商运营、市场分析等场景。遵守法规、注意调用频率限制及数据安全,确保应用的稳定性和合法性。
Socket.IO介绍,以及怎么连接测试Socket.IO接口?
Socket.IO 是一个用于浏览器和服务器间实时双向通信的库,支持低延迟消息传递、跨平台运行及自动重连。文章介绍了其特点与调试需求,并详细说明如何使用 Apifox 工具创建、连接、发送/接收 Socket.IO 事件,以及团队协作和调试技巧。掌握这些技能可提升实时应用开发效率与质量。
微信公众号接口测试实战指南
微信公众号接口测试是确保系统稳定性和功能完整性的重要环节。本文详细介绍了测试全流程,包括准备、工具选择(如Postman、JMeter)、用例设计与执行,以及常见问题的解决方法。通过全面测试,可以提前发现潜在问题,优化用户体验,确保公众号上线后稳定运行。内容涵盖基础接口、高级接口、微信支付和数据统计接口的测试,强调了功能验证、性能优化、安全保护及用户体验的重要性。未来,随着微信生态的发展,接口测试将面临更多挑战和机遇,如小程序融合、AI应用和国际化拓展。
Midscene.js:AI 驱动的 UI 自动化测试框架,支持自然语言交互,生成可视化报告
Midscene.js 是一款基于 AI 技术的 UI 自动化测试框架,通过自然语言交互简化测试流程,支持动作执行、数据查询和页面断言,提供可视化报告,适用于多种应用场景。
1205 1
Midscene.js:AI 驱动的 UI 自动化测试框架,支持自然语言交互,生成可视化报告
pytest接口自动化测试框架搭建
通过上述步骤,我们成功搭建了一个基于 `pytest`的接口自动化测试框架。这个框架具备良好的扩展性和可维护性,能够高效地管理和执行API测试。通过封装HTTP请求逻辑、使用 `conftest.py`定义共享资源和前置条件,并利用 `pytest.ini`进行配置管理,可以大幅提高测试的自动化程度和执行效率。希望本文能为您的测试工作提供实用的指导和帮助。
167 15
接口测试工具攻略:轻松掌握测试技巧
在互联网快速发展的今天,软件系统的复杂性不断增加,接口测试工具成为确保系统稳定性的关键。它如同“翻译官”,模拟请求、解析响应、验证结果、测试性能并支持自动化测试,确保不同系统间信息传递的准确性和完整性。通过Apifox等工具,设计和执行测试用例更加便捷高效。接口测试是保障系统稳定运行的第一道防线。

热门文章

最新文章

AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等