http interface automated testing framework implementation

1. Description of test requirements
A series of http interface function tests on the service backend.

Input: Construct different parameter input values according to the interface description

Output: XML file

eg:http://xxx.com/xxx_product/test/content_book_list.jsp?listid=1

2. Implementation method
1. Use Python scripts to drive tests

2. Use Excel tables to manage test data, including use case management, test data entry, test result display, etc. This requires encapsulating an Excel class.

3. Call the http interface and use the API encapsulated in Python.

4. Just convert the http assembly characters required for testing

5. Set 2 checkpoints, the return value field in the XML file (obtained by parsing XML); the correctness of the XML file (file comparison)

6. The first execution test adopts a semi-automated method, that is, manually checking whether the output XML file is correct. Once it is correct, the XML file will be sealed as the expected result of the subsequent regression test. If an error is found, it will be manually corrected to the expected file. (Note that this file is not manually checked for every test, only the first time it is tested)

3. Excel table style
4. Implement the code (code is king, and it can be easily understood with comments)
1. Test framework code

#********************************************** ******************
#TestFrame.py
# Author : Vince
# Version: 1.1.2
# Date: 2011-3-14
# Description: Automated testing platform
#************************************************ ***************
  
import os,sys, urllib, httplib, profile, datetime, time
from xml2dict import XML2Dict
import win32com.client
from win32com.client import Dispatch
import xml.etree.ElementTree as et
#import MySQLdb
  
#Background color of test results in Excel table
OK_COLOR=0xffffff
NG_COLOR=0xff
#NT_COLOR=0xffff
NT_COLOR=0xC0C0C0
  
#The summary display position of test results in Excel table
TESTTIME=[1, 14]
TESTRESULT=[2, 14]
  
#Excel template settings
#self.titleindex=3 #Test case title row index in Excel
#self.casebegin =4 #Test case start row index in Excel
#self.argbegin =3 #The parameter starting column index in Excel
#self.argcount =8 #Number of parameters supported in Excel
class create_excel:
    def __init__(self, sFile, dtitleindex=3, dcasebegin=4, dargbegin=3, dargcount=8):
        self.xlApp = win32com.client.Dispatch('et.Application') #MS:Excel WPS:et
        try:
            self.book = self.xlApp.Workbooks.Open(sFile)
        except:
            print_error_info()
            print "Failed to open file"
            exit()
        self.file=sFile
        self.titleindex=dtitleindex
        self.casebegin=dcasebegin
        self.argbegin=dargbegin
        self.argcount=dargcount
        self.allresult=[]
          
        self.retCol=self.argbegin + self.argcount
        self.xmlCol=self.retCol + 1
        self.resultCol=self.xmlCol + 1
  
    def close(self):
        #self.book.Close(SaveChanges=0)
        self.book.Save()
        self.book.Close()
        #self.xlApp.Quit()
        del self.xlApp
          
    def read_data(self, iSheet, iRow, iCol):
        try:
            sht = self.book.Worksheets(iSheet)
            sValue=str(sht.Cells(iRow, iCol).Value)
        except:
            self.close()
            print('Failed to read data')
            exit()
        #Remove'.0'
        if sValue[-2:]=='.0':
            sValue = sValue[0:-2]
        return sValue
  
    def write_data(self, iSheet, iRow, iCol, sData, color=OK_COLOR):
        try:
            sht = self.book.Worksheets(iSheet)
            sht.Cells(iRow, iCol).Value = sData.decode("utf-8")
            sht.Cells(iRow, iCol).Interior.Color=color
            self.book.Save()
        except:
            self.close()
            print('Failed to write data')
            exit()
      
    #Get the number of use cases
    def get_ncase(self, iSheet):
        try:
            return self.get_nrows(iSheet)-self.casebegin + 1
        except:
            self.close()
            print('Failed to obtain Case number')
            exit()
      
    def get_nrows(self, iSheet):
        try:
            sht = self.book.Worksheets(iSheet)
            return sht.UsedRange.Rows.Count
        except:
            self.close()
            print('Failed to obtain nrows')
            exit()
  
    def get_ncols(self, iSheet):
        try:
            sht = self.book.Worksheets(iSheet)
            return sht.UsedRange.Columns.Count
        except:
            self.close()
            print('Failed to obtain ncols')
            exit()
      
    def del_testrecord(self, suiteid):
        try:
            #Specially extracted from the For loop to improve performance
            nrows=self.get_nrows(suiteid) + 1
            ncols=self.get_ncols(suiteid) + 1
            begincol=self.argbegin + self.argcount
              
            #Improve performance
            sht = self.book.Worksheets(suiteid)
  
            for row in range(self.casebegin, nrows):
                for col in range(begincol, ncols):
                    str=self.read_data(suiteid, row, col)
                    #Clear actual results[]
                    startpos = str.find('[')
                    if startpos>0:
                        str = str[0:startpos].strip()
                        self.write_data(suiteid, row, col, str, OK_COLOR)
                    else:
                        #Improve performance
                        sht.Cells(row, col).Interior.Color = OK_COLOR
                #Clear the test results in the TestResul column and set it to NT
                self.write_data(suiteid, row, self.argbegin + self.argcount + 1, ' ', OK_COLOR)
                self.write_data(suiteid, row, self.resultCol, 'NT', NT_COLOR)
        except:
            self.close()
            print('Clear data failed')
            exit()
  
#Execute call
def HTTPInvoke(IPPort, url):
    conn = httplib.HTTPConnection(IPPort)
    conn.request("GET", url)
    rsps = conn.getresponse()
    data = rsps.read()
    conn.close()
    return data
  
#Get the basic information of the use case [Interface,argcount,[ArgNameList]]
def get_caseinfo(Data, SuiteID):
    caseinfolist=[]
    sInterface=Data.read_data(SuiteID, 1, 2)
    argcount=int(Data.read_data(SuiteID, 2, 2))
      
    #Get the parameter name and store it in ArgNameList
    ArgNameList=[]
    for i in range(0, argcount):
        ArgNameList.append(Data.read_data(SuiteID, Data.titleindex, Data.argbegin + i))
      
    caseinfolist.append(sInterface)
    caseinfolist.append(argcount)
    caseinfolist.append(ArgNameList)
    return caseinfolist
  
#Get input
def get_input(Data, SuiteID, CaseID, caseinfolist):
    sArge=''
    #Parameter combination
    for j in range(0, caseinfolist[1]):
        if Data.read_data(SuiteID, Data.casebegin + CaseID, Data.argbegin + j) != "None":
            sArge=sArge + caseinfolist[2][j] + '=' + Data.read_data(SuiteID, Data.casebegin + CaseID, Data.argbegin + j) + ' & amp;'
      
    #Remove the trailing & amp; characters
    if sArge[-1:]==' & amp;':
        sArge = sArge[0:-1]
    sInput=caseinfolist[0] + sArge #Combine all parameters
    return sInput
   
#Result judgment
def assert_result(sReal, sExpect):
    sReal=str(sReal)
    sExpect=str(sExpect)
    if sReal==sExpect:
        return 'OK'
    else:
        return 'NG'
  
#Write test results to file
def write_result(Data, SuiteId, CaseId, resultcol, *result):
    if len(result)>1:
        ret='OK'
        for i in range(0, len(result)):
            if result[i]=='NG':
                ret='NG'
                break
        if ret=='NG':
            Data.write_data(SuiteId, Data.casebegin + CaseId, resultcol,ret, NG_COLOR)
        else:
            Data.write_data(SuiteId, Data.casebegin + CaseId, resultcol,ret, OK_COLOR)
        Data.allresult.append(ret)
    else:
        if result[0]=='NG':
            Data.write_data(SuiteId, Data.casebegin + CaseId, resultcol,result[0], NG_COLOR)
        elif result[0]=='OK':
            Data.write_data(SuiteId, Data.casebegin + CaseId, resultcol,result[0], OK_COLOR)
        else: #NT
            Data.write_data(SuiteId, Data.casebegin + CaseId, resultcol,result[0], NT_COLOR)
        Data.allresult.append(result[0])
      
    #Print the current result immediately
    print 'case' + str(CaseId + 1) + ':', Data.allresult[-1]
  
#Print test results
def statisticresult(excelobj):
    allresultlist=excelobj.allresult
    count=[0, 0, 0]
    for i in range(0, len(allresultlist)):
        #print 'case' + str(i + 1) + ':', allresultlist[i]
        count=countflag(allresultlist[i],count[0], count[1], count[2])
    print 'Statistic result as follow:'
    print 'OK:', count[0]
    print 'NG:', count[1]
    print 'NT:', count[2]
  
#Parse XmlString and return Dict
def get_xmlstring_dict(xml_string):
    xml = XML2Dict()
    return xml.fromstring(xml_string)
      
#Parse XmlFile and return Dict
def get_xmlfile_dict(xml_file):
    xml = XML2Dict()
    return xml.parse(xml_file)
  
#Remove historical data expect[real]
def delcomment(excelobj, suiteid, iRow, iCol, str):
    startpos = str.find('[')
    if startpos>0:
        str = str[0:startpos].strip()
        excelobj.write_data(suiteid, iRow, iCol, str, OK_COLOR)
    return str
      
#Check each item (non-structure)
def check_item(excelobj, suiteid, caseid,real_dict, checklist, begincol):
    ret='OK'
    for checkid in range(0, len(checklist)):
        real=real_dict[checklist[checkid]]['value']
        expect=excelobj.read_data(suiteid, excelobj.casebegin + caseid, begincol + checkid)
          
        #If the check is inconsistent, write the actual result into the expect field, format: expect[real]
        #will return NG
        result=assert_result(real, expect)
        if result=='NG':
            writestr=expect + '[' + real + ']'
            excelobj.write_data(suiteid, excelobj.casebegin + caseid, begincol + checkid, writestr, NG_COLOR)
            ret='NG'
    return ret
  
#Check structure type
def check_struct_item(excelobj, suiteid, caseid,real_struct_dict, structlist, structbegin, structcount):
    ret='OK'
    if structcount>1: #The passed in is List
        for structid in range(0, structcount):
            structdict=real_struct_dict[structid]
            temp=check_item(excelobj, suiteid, caseid,structdict, structlist, structbegin + structid*len(structlist))
            if temp=='NG':
                ret='NG'
                       
    else: #The passed in is Dict
        temp=check_item(excelobj, suiteid, caseid,real_struct_dict, structlist, structbegin)
        if temp=='NG':
            ret='NG'
              
    return ret
  
#Get the exception function and line number
def print_error_info():
    """Return the frame object for the caller's stack frame."""
    try:
        raiseException
    except:
        f = sys.exc_info()[2].tb_frame.f_back
    print (f.f_code.co_name, f.f_lineno)
  
#Test result counter, similar to Switch statement implementation
def countflag(flag,ok, ng, nt):
    calculation = {'OK':lambda:[ok + 1, ng, nt],
                         'NG':lambda:[ok, ng + 1, nt],
                         'NT':lambda:[ok, ng, nt + 1]}
    return calculation[flag]() 

2. Project test code

# -*- coding: utf-8 -*-
#************************************************ ***************
#xxx_server_case.py
# Author : Vince
# Version: 1.0
# Date: 2011-3-10
# Description: Content service system test code
#************************************************ ***************
  
from testframe import *
from common_lib import *
  
httpString='http://xxx.com/xxx_product/test/'
expectXmldir=os.getcwd() + '/TestDir/expect/'
realXmldir=os.getcwd() + '/TestDir/real/'
  
def run(interface_name, suiteid):
    print '【' + interface_name + '】' + ' Test Begin,please waiting...'
    global expectXmldir, realXmldir
      
    #Create the expected result directory and actual result directory respectively based on the interface name
    expectDir=expectXmldir + interface_name
    realDir=realXmldir + interface_name
    if os.path.exists(expectDir) == 0:
        os.makedirs(expectDir)
    if os.path.exists(realDir) == 0:
        os.makedirs(realDir)
      
    excelobj.del_testrecord(suiteid) #Clear historical test data
    casecount=excelobj.get_ncase(suiteid) #Get the number of cases
    caseinfolist=get_caseinfo(excelobj, suiteid) #Get basic information of Case
      
    #Traverse execution cases
    for caseid in range(0, casecount):
        #Check whether to execute the Case
        if excelobj.read_data(suiteid,excelobj.casebegin + caseid, 2)=='N':
            write_result(excelobj, suiteid, caseid, excelobj.resultCol, 'NT')
            continue #The current Case ends and continues to execute the next Case
          
        #Get test data
        sInput=httpString + get_input(excelobj, suiteid, caseid, caseinfolist)
        XmlString=HTTPInvoke(com_ipport, sInput) #Execute the call
          
        #Get the return code and compare
        result_code=et.fromstring(XmlString).find("result_code").text
        ret1=check_result(excelobj, suiteid, caseid,result_code, excelobj.retCol)
          
        #Save the expected result file
        expectPath=expectDir + '/' + str(caseid + 1) + '.xml'
        #saveXmlfile(expectPath, XmlString)
          
        #Save the actual results file
        realPath=realDir + '/' + str(caseid + 1) + '.xml'
        saveXmlfile(realPath, XmlString)
          
        #Compare expected results and actual results
        ret2= check_xmlfile(excelobj, suiteid, caseid,expectPath, realPath)
          
        #Write test results
        write_result(excelobj, suiteid, caseid, excelobj.resultCol, ret1, ret2)
    print '【' + interface_name + '】' + ' Test End!' 

3. Test entrance

# -*- coding: utf-8 -*-
#************************************************ ***************
#main.py
# Author : Vince
# Version: 1.0
# Date: 2011-3-16
# Description: Test assembly, use case execution entry
#************************************************ ***************
  
from testframe import *
from xxx_server_case import *
import xxx_server_case
  
#Product system interface test
#Set up test environment
xxx_server_case.excelobj=create_excel(os.getcwd() + '/TestDir/xxx_Testcase.xls')
xxx_server_case.com_ipport=xxx.com'
  
#Add testsuite begin
run("xxx_book_list", 4)
#Add other suite from here
#Add testsuite end
  
statisticresult(xxx_server_case.excelobj)
xxx_server_case.excelobj.close() 
Summary:

Thank you to everyone who reads my article carefully! ! !

As someone who has experienced it, I also hope that everyone will avoid some detours. If you don’t want to experience the feeling of not being able to find information, no one to answer questions, and giving up after a few days of study, here I will share with you some learning about automated testing. Resources, I hope they can help you on your way forward.

How to obtain documents:

Join my software testing exchange group: 632880530 Get it for free~ (Academic exchanges with fellow experts, and live broadcasts every night to share technical knowledge points)

This document should be the most comprehensive and complete preparation warehouse for friends who want to engage in [software testing]. This warehouse has also accompanied me through the most difficult journey. I hope it can also help you!

All of the above can be shared. You only need to search the vx official account: Programmer Hugo to get it for free