Python builds an interface (API) automated testing framework from scratch

Transferred from: https://www.csdn.net/tags/MtTaMgwsMTg2MjI4LWJsb2cO0O0O.html

Python builds an interface (API) automated testing framework from scratch 2021-01-03 23:51:03

  • catalogue

    1. Foreword

    2. Train of thought

    3. Text

    1, Path module - initpath py

    2, Configuration file module - getconfig py

    3, Read the use case module - getcase py

    4, Database operation module - operatordb py

    5, Log module - log py

    6, Mail module - sendemail py

    7, Message module - sendmsg py

    8, Variable parameterization module - parameterization py

    9, API request module - sendapirequests py

    10, Module of public method (core) - commkeyword py

    11, Factory packaging (core) - methodfactory py

    12, Analyze use case (core) - testcase py

    13, The last run file - testrun py

     

    1. Foreword

    Automated testing is an indispensable and important part of the testing road. Now there are many automated testing tools that can basically meet the testing requirements of the software market, but the use of tools makes people know it, but they don't know why. After learning it, they are only an automated testing tool person. Therefore, learning the automation framework is the only way to get rid of the tool person, improve themselves and get a raise and promotion;

    King Andy Lau said: learn to teach people.

    This is a bit of knowledge shared by someone who has learned it. I hope to test my compatriots. If you feel a little help when passing by my blog on the way forward, please leave your praise.

    When viewing this article, you need to have an entry-level Python code foundation. If not, please go to the video tutorial for some time.

    All the explanation codes and execution results in this article are shown in screenshots, so that you can see the process of writing code and improve yourself; Of course, if you don't want to write, you can just jump to the end of each section and show the overall code. You can "use the programmer's high-level skills ctrl+c, ctrl+v" to learn by yourself.

     

    Code base: Soul tail / testApi

    2. Train of thought

    1. Build a directory framework

    As shown in the figure below

         

    Meaning of contents in common directory

                 setApirequest.py , implement the module of API interface request and send post/get and other methods;       

                 getCase.py , realize the of reading the use case file data in the data directory, design the execution format, and output the JSON use case set;

                 getConfig.py to read the configuration file in the config directory;

                 initPath.py to obtain the path of the framework file, which is convenient for other modules to use the directory method;

                 log.py , realize the unified entry of log printing and output files to the log directory;

                 operatorDB.py) the method of reading and writing data;

                 parameteriZation.py entity class to realize data parameterization;

                 sendEmail.py\ SendMsg.py) the method of sending test reports to e-mail and enterprise wechat in real time;

    Meaning of contents in kemel directory

                 methodFactory.py realizes the unified entrance of each method call;

                 commKeyworl.py public method is mainly the operation entrance of sub packaging all bottom modules, and provides some special public methods;

    Meaning of contents in testcase directory

    There are no files added to the directory in the picture. It should be this file tsetcase Py to realize the resolution of getcase The JSON use case set output in. Py, the module that executes the use case, and the module that checks the execution result. Because of the requirements of the Unitest library, the use case analysis directory and file must start with test. Of course, see the meaning of the name.

    Other directories

    Data case file directory, log output log directory, report output test report directory, and library introduces the directory of three-party modules (ddt data-driven module, HTMLRunner test report format output module)

    library introduces the download path of two third-party libraries (important files, not or missing):

                 ddt:Automatic generation test data driven DDT Py - Internet document resources - CSDN Download

                 HTMLRunner:   HTMLTestRunnerNew.py is a test report on Python's interface (API) automation test framework from scratch. Do not download it without this framework_ htmltestrunnernew Internet document resources CSDN Download

       

    5. Layered concept

    A hierarchical concept of software MCV

    M is the bottom layer, model layer, code that encapsulates functions, and belongs to the bottom layer code.

    C is the middle layer and control layer, which encapsulates all the underlying access methods.

    V is the high-level, session level, run execution and other method operations.

    Differentiation of MVC (layered concept)

    All modules in the common directory are M

    The module in kemel directory is C

    test_Case.py, testRun.py and other operation modules are V

                

    3. Text

    1, Path module - initpath py

    1. Learn how to obtain the current absolute path:

    Let's first understand how python obtains the current directory. We can use the built-in library os to implement it

    First we need to know__ file__ Represent the current file in python,

    Then find OS in the rookie tutorial Path library, know the method to obtain the absolute path OS path. abspath(path)

    Then start at initpath Py write code and print OS path. abspath(__file__) method

    As shown in the figure below, initpath is printed out Absolute path of py.

    2. Obtain the file path, excluding its own file name

    Found OS in the rookie Path, dirname() method

    In initpath Use this method to print in py, and then we hope it is the path of \, so we use OS path. dirname(os.path.abspath(__file__)) This code takes the path

    Then we add another layer, OS path. dirname(os.path.dirname(os.path.abspath(__file__))), We want to get the project root directory path. As shown below

    3. Splice all paths

    We need a way to splice directories. Go to rookie.com and find OS path. Join (path1, path2), synthetic path method

    Write initpath directly Py code, which defines the paths of all folders under the project. Print as shown in the figure below

     

                 initPath. Snippet of PY

    import os
    #get project dir
    BASEDIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
    #get common dir
    COMMONDIR = os.path.join(BASEDIR, 'common')
    #get config dir
    CONFDIR = os.path.join(BASEDIR, 'config')
    #get data dir
    DATADIR = os.path.join(BASEDIR, 'data')
    #get library dir
    LIBDIR = os.path.join(BASEDIR, 'library')
    #get log dir
    LOGDIR = os.path.join(BASEDIR, 'log')
    #get report dir
    REPORTDIR = os.path.join(BASEDIR, 'report')
    #get testcaset dir
    CASEDIR = os.path.join(BASEDIR, 'testcases')

     

     

    2, Configuration file module - getconfig py

    1. Let's learn about the configuration file first

    In the computer field, configuration file: a computer file that can configure parameters and initial settings for some computer programs.

    The specific file types and related knowledge points are not enough. We use an ini configuration file here, and the content format is as follows

                 [about]

                 aaa = bbb

                 ccc = ddd

    In fact, about is a node, aaa = bbb is a parameter, and key = value means

    So in config / basecon Add some configurations under ini, as shown in the following figure

    2. Read basecon Configuration items in ini

    Get profile path:

    Before reading the configuration file, we have to get the file basecon The absolute path of ini,

    First reference the absolute path of the defined config, and then use OS path. The join (path1, path2) method will basecon The absolute path of ini is generated. The specific code is shown in the figure below

    Understanding configparser

    ConfigParser is a package used to read configuration files. Third party libraries, so you need to download them through the command pip install configparser.

    Directly import the ConfigParser class into the code, then create its object, call the method read() to read the configuration file, and the specific code is shown in the figure below

    After carefully studying the above path, we will begin to encapsulate the configparser. This library is perfect, but we need to omit the step of reading the file when reading the configuration file later, so we need to encapsulate it a little.

    Encapsulating configparser configuration file reading

    The encapsulated code runs as shown in the figure below

                 

                 nyCof. The savedata () method adds a configuration item and writes it to basecon Ini inside

                getConfig. Snippet of Py:

    import os
    from common.initPath import CONFDIR
    from configparser import ConfigParser
    # conPath = os.path.join(CONFDIR, 'baseCon.ini')
    # print(conPath)
    # cnf = ConfigParser()
    # cnf.read(conPath, encoding='utf-8')   #The first parameter is the file path, and the second parameter is the read encoding
    #
    # print('baseCon.ini All nodes in the{}'.format(cnf.sections())) #Print all node names
    # print('db All under key:{}'.format(cnf.options('db')))  #Print all key s under db node
    # print('db All under Item:{}'.format(cnf.items('db')))    #Print all item s under db node
    # print('db Lower host of value:{}'.format(cnf.get('db', 'host')))  #Print a value of a node
    
    """
    definition Config continue ConfigParser
    """
    class Config(ConfigParser):
    
        def __init__(self):
            """
            initialization
            Read out the configuration file
            super().    Call parent class
            """
            self.conf_name = os.path.join(CONFDIR, 'baseCon.ini')
            super().__init__()
            super().read(self.conf_name, encoding='utf-8')
    
        def getAllsections(self):
            """
            :return: Returns all node names
            """
            return super().sections()
    
        def getOptions(self, sectioName):
            """
            :param sectioName: Node name
            :return: Returns all of the nodes key
            """
            return super().options(sectioName)
    
        def getItems(self, sectioName):
            """
            :param sectioName: Node name
            :return: Returns all of the nodes item
            """
            return super().items(sectioName)
    
        def getValue(self, sectioName, key):
            """
            :param sectioName: The name of the node
            :param key: key name
            :return: return sectioName lower key of value
            """
            return super().get(sectioName, key)
    
        def saveData(self, sectioName, key, value):
            """
            Add configuration
            :param sectioName: Node name
            :param key: key name
            :param value: value
            :return:
            """
            super().set(section=sectioName, option=key, value=value)
            super().write(fp=open(self.conf_name, 'w'))
    
    
    myCof = Config()
    #print(myCof.getAllsections())
    #print(myCof.getOptions('db'))
    #print(myCof.getItems('db'))
    #print(myCof.getValue('db', 'host'))
    #myCof.saveData('db', 'newKey', 'newValue')

     

     

    3, Read the use case module - getcase py

    Reading use cases is an important and independent module of the framework. However, before reading use cases, we must first design the use case file and user format. Generally, we can put the use cases in excel or establish a mysql database to import the data in excel, but the latter is more troublesome, so this paper only reads the use cases in excel files

    1. Design case file and use case format

    Add a testcase.exe in the data directory The excel files of xlsx are listed in the following figure:

    Open testcase Xlsx, design the use case header in sheet1, as shown in the following figure:

    Meaning of use case field:

                  case_id: id, set the number yourself, just to identify the location of the use case when outputting the report

    api: the interface route in url is also used to identify the correctness of use cases when outputting reports

    Title: it can be the Chinese title of the use case, or the variable of parameterized received data in the user-defined method. The format can be abc or ${abc}

    Method: request type post/get, or custom method name

    url: the url in the interface request, which can be parameterized

    Headers: the headers in the interface request can be parameterized

    Data: data in the interface request, which can be parameterized

    checkey: check method of use case checkpoint

    expected: the check value of the use case checkpoint

                  Test_result: after the use case is executed, write back the status of the use case, pass/fail, but I don't usually use it.

    For example, the following figure:

              

    2. Design case set JSON format

    The format is as follows:

                 [   #Use case set
    
                      {    #First use case
    
                         key1: value1,    #Fields and field values in use cases
    
                         key2: value2
    
                         ...
    
                       },
    
                       {  #Second use case
    
                           key1:value1,  #Fields and fields in use cases
    
                           key2: value2
    
                           ...
    
                        },
    
                        .... 
    
                  ]

    According to the use case excel file, the specific JSON use case string should look like this. The front field is excel list data, and the last one is sheet_name is the name of the sheet page. General interface use cases are written according to modules, with one sheet page for each module, which is distinguished by this field to prepare for the subsequent output of test reports.

    [

          {

                'case_id': 1,

                'api':  'publickey',

                 'title': 'url',

    'method ':' set variable ',

                 'url': 'https://www.adbcde.com.cn/',

                 'headers': 'Host:www.adbcde.com.cn',

                 'data': 'userToken=16029513602050&loginpassword=qwert12345&loginphone=15361828291',

    'checkey': 'the result contains',

                 'expected': '"return_code": "SUCCESS"',

                 'Test result': None,

                 'sheet_name': 'sheet1',

           },

           {

              ......

           }

    .....

    ]

     

    3. openpyxl Library Learning

    There are many libraries for reading and writing excel. Because the use case requirements are not high, openpyxl library is selected to encapsulate the use case code

    The installation command is pip install openpyxl

    Start writing code

    Before operating the excel use case, you should get the absolute path of the file

    Get the absolute path successfully, and then start reading the files in excel,

    Read excel file content

    The steps are as follows: 1. Open the excel file first, 2. Traverse the sheet page to output the name, and 3. Traverse the data in sheet 1. After these three are written, the table name will be. Excel can be read successfully. openpyxl is finished

    4. Encapsulate use case classes

    The use case I defined is an excel file and a project. In work, there should be more than one project, so there may be multiple Excel files in the data directory. Which one do we need to execute? Therefore, the configuration file written above can be used. Add a case node in it and add the name of the execution use case file

    There must be many annotated use cases in a use case file, so define one # to distinguish annotated use cases. The two definitions are shown in the figure below

    The encapsulated code can't carry all the diagrams. Look at the execution results first, and then read the use case. It only needs two lines of code to read the use case JSON

                         getCase. Snippet of PY

    import os
    import openpyxl
    from common.initPath import DATADIR
    from common.getConfig import myCof
    
    # #Absolute path of splicing case file
    # caseFile = os.path.join(DATADIR, 'testcase.xlsx')
    # print(caseFile)
    # #Read excel file
    # xl = openpyxl.open(filename=caseFile)
    # #Print the sheet page name in caseFile
    # print('print all sheet pages')
    # for sheet in  xl:
    #     print(sheet.title)
    # #Print all row field data in excel
    # print('print all row field data in Sheet1 ')
    # sh = xl['Sheet1']
    # data = list(sh.rows)
    # for da in data:
    #     for k in da:
    #         print(k.value)
    
    
    class Getcase(object):
        def __init__(self, sheet_name=None):
            """
            Initialization file name, sheet name,excel Opposite image
            :param sheet_name: Incoming sheet Name; can be blank.
            """
            filename = myCof.getValue('case', 'testCase')
            self.note = myCof.getValue('identifier', 'note')
            self.caseFile = os.path.join(DATADIR, filename)
            self.sheet_name = sheet_name
            self.wb = None
    
        def openexcel(self):
            """
            open excel file
            If sheet The name cannot be empty, locate the corresponding sheet page
            :return:
            """
            self.wb = openpyxl.open(self.caseFile)
            if self.sheet_name is not None:
                self.sh = self.wb[self.sheet_name]
    
    
        def read_excels(self):
            """
            Format use case set
            Use case format JSON See the previous description above
            Filter out#Annotated use cases
            :return:
            """
            if self.wb is None:
                self.openexcel()
            datas = list(self.sh.rows)
            title = [i.value for i in datas[0]]
            cases = []
            for i in datas[1:]:
                data = [k.value for k in i]
                case = dict(zip(title, data))   #Format data into JSON string
                try:
                    if str(case['case_id'])[0] is not self.note:  # Filter out the use cases that begin with the note symbol, and comment out the cases that are not collected or executed
                        case['sheet'] = self.sh.title
                        cases.append(case)
                except KeyError:
                    cases.append(case)
            return cases
    
        def read_all_excels(self):
            """
            Traverse all sheet page
            Get all use case sets and format them next time,
            Filter out#Annotated sheet page
            :return:
            """
            self.openexcel()
            cases = []
            for sheet in self.wb:
                if sheet.title[0] is not self.note:  # Filter out the sheet page beginning with the note symbol, and the annotated ones will not be collected and executed
                    self.sh = sheet
                    cases += self.read_excels()
            return cases
    
    
        def write_excels(self, rows, column, value):
            """
            Write back use case fields
            :param rows:
            :param column:
            :param value:
            :return:
            """
            self.openexcel()
            self.sh.cell(row=rows, column=column, value=value)
            self.wb.save(self.caseFile)
    
    
    # readExce = Getcase()
    # print(readExce.read_all_excels())
    

     

    4, Database operation module - operatordb py

    1. pymysql installation

    To install pymysql, use the command pip install pymysql

     

    2. pymysql learning

    Before learning, we first take out the relevant configuration parameters connected to the database in the configuration file

    As shown in the figure below, first import the myCof object, use getValue to get the corresponding configuration parameters, and print successfully

    pymysql has been imported successfully, and the connection data parameters have been taken out. Next, start connecting data and executing SQL statements

    Connect database

    If the connection is not successful, an exception will be thrown, so you need to try except..... To catch the exception, print it out. The following figure shows the timeout of connecting to the database, because the Mysql service has not been started yet.

    Here, we have to configure a mysql server to facilitate debugging. Those who don't understand can find tutorials online to learn. Of course, it's OK to directly use the company's testing environment, which saves time and effort.

    After opening the mysql service and running, an error will be reported. We strongly convert the port parameter to int type, which cannot be str type.

    After the strong switch, run it again and the connection is successful

    Execute SQL

    To execute SQL statements, we need a cursor. First obtain the cursor, and then use the cursor to execute SQL. It passes at one time

    Print data of SQL query

    After learning pymysql, you can encapsulate it

    3. Package data operation

    The encapsulated logic is to use the connection as a method, execute SQL to write a method, and close the connection to write a method

    First look at the execution results after encapsulation. You only need to write four lines of code to execute SQL statements.

                    opeartorDB. Snippet of Py:

    import pymysql
    from common.getConfig import myCof
    
    # host = myCof.getValue('db', 'host')
    # port = int(myCof.getValue('db', 'port'))
    # user = myCof.getValue('db', 'user')
    # pwd = myCof.getValue('db', 'pwd')
    # database = myCof.getValue('db', 'database')
    # charset = myCof.getValue('db', 'charset')
    # try:
    #     #Connect database
    #     db = pymysql.connect(host=host, port=port, user=user, password=pwd, database=database, charset=charset)
    #     #Get cursor
    #     cursor = db.cursor()  
    #     #Execute SQL
    #     cursor.execute("select * from Student where SName = 'Lin Xiaoliu';")
    #     #Get query results
    #     result = cursor.fetchall()
    #     #Print query results
    #     print(result)
    #     print('execution succeeded ')
    # except Exception as e:
    #     print('connection failed, reason: {} '. format(str(e)))
    """
    encapsulation mysql operation
    """
    class OpeartorDB(object):
        def __init__(self):
            """
            Initialization method, keep it habitually
            """
            pass
    
        def connectDB(self):
            """
            Connect database
            :return: Return success or failure, reason
            """
            host = myCof.getValue('db', 'host')
            port = myCof.getValue('db', 'port')
            user = myCof.getValue('db', 'user')
            pwd = myCof.getValue('db', 'pwd')
            database = myCof.getValue('db', 'database')
            charset = myCof.getValue('db', 'charset')
            try:
                self.db = pymysql.connect(host=host, port=int(port), user=user, password=pwd, database=database, charset=charset)
                return True, 'Successfully connected data'
            except Exception as e:
                return False, 'Failed to connect data[' + str(e) + ']'
    
    
        def closeDB(self):
            """
            Close the data connection. If it is not closed, the number of data connections cannot be released and the database performance will be affected
            :return:
            """
            self.db.close()
    
        def excetSql(self, enpsql):
            """
            implement sql method,
            :param enpsql: Incoming sql sentence
            :return: Return success and execution result or failure and failure reason
            """
            isOK, result = self.connectDB()
            if isOK is False:
                return isOK, result
            try:
                cursor = self.db.cursor()
                cursor.execute(enpsql)
                res = cursor.fetchone()     #In order to speed up the automatic test, only one piece of data is taken for the general scenario
                if res is not None and 'select' in enpsql.lower():    #Judge whether it is a query,
                    des = cursor.description[0]
                    result = dict(zip(des, res))   #Format the returned data into a JSON string
                elif res is None and ('insert' in enpsql.lower() or 'update' in enpsql.lower()):               #Determine whether to insert or update data
                    self.db.commit()       #Submit the data operation, or insert or update the data. The data will only be updated in the cache without officially falling into the database
                    result = ''       #Operation data, no need to return data
                cursor.close()      #Close cursor
                self.closeDB()      #Close data connection
                return True, result
            except Exception as e:
                return False, 'SQL Execution failed,reason:[' + str(e) + ']'
    
    
    
    
    
    # sql = 'select * from Student'
    # oper = OpeartorDB()
    # isOK, result = oper.excetSql(sql)
    # print(result)
    
    
    

     

    5, Log module - log py

     

    1. logging learning

    logging is the basic library of python. It does not need to be downloaded and can be imported directly

    There are five levels of logs. The automatic test is generally printed with INFO, so we add the log parameter configuration in the configuration file

                    [log]

                    level = INFO

    Print log

    When writing code, first obtain the log level configuration, then set the log level, initialize the log object and print the log. Because the log is INFO, the debug log will not be printed. The code is shown in the figure below

    Format log

    The format setting is shown in the figure below

     

     

    Output log to file

    The log files are stored in the log directory, so get the import directory and os first

    Every other day, another log will be added and kept for 15 days. Therefore, it is necessary to import a method in logging, TimedRotatingFileHandler

    from logging. Handlers import method of timedrotatingfilehandler # import

    The code is shown in the figure below

    After running, the testReport file is output, in which the execution log is printed

    The basic learning of logging is completed, and then it is simply encapsulated

     

    2. Log module encapsulation

    There is no need to create an object to encapsulate the Log, because it needs to return a logging object, object operation object, which is awkward. Therefore, a static method can be directly encapsulated in the Log class, and a logging object can be returned directly by calling the method of the class.

    The result of mode execution is consistent with the above, but the screenshot is not needed, and the code is directly

                   log.py code snippet

    import os
    import logging
    from common.getConfig import myCof
    from common.initPath import LOGDIR
    from logging.handlers import TimedRotatingFileHandler
    
    # # Get configuration parameters such as logs
    # level = myCof.getValue('log', 'level')
    # # Set the log format,% (actime) s represents the time,% (name)s represents the incoming identification name,% (levelname)s represents the log level,% (message)s represents the log message
    # format = '%(asctime)s - %(name)s-%(levelname)s: %(message)s'
    # # Set basic level of log
    # logging.basicConfig(level=level, format=format)
    # # Initialize the log object. Hunwei is name
    # mylog = logging.getLogger('Hunwei')
    # #Splice log directory
    # log_path = os.path.join(LOGDIR, 'testReport')
    # #Generate a file handle, filename is the file path, when table is the time, D indicates days, up to 15 log files in the backuCount=15 directory, encoding ='utf-8 'log character format
    # fh = TimedRotatingFileHandler(filename=log_path, when="D", backupCount=15, encoding='utf-8')
    # #Set the format of the history log file name, and the corresponding log will be automatically generated on a certain day
    # fh.suffix = "%Y-%m-%d.log"
    # #Set the log level of file output
    # fh.setLevel(level)
    # #Set the log format for file output
    # fh.setFormatter(logging.Formatter("%(asctime)s - %(name)s-%(levelname)s: %(message)s"))
    # #Add file handle to log object
    # mylog.addHandler(fh)
    #
    # mylog.debug('debug')
    # mylog.info('info')
    # mylog.warn('warm')
    # mylog.error('error')
    # mylog.fatal('fatal')
    
    
    
    class Log(object):
    
        @staticmethod
        def getMylog():
            # Get configuration parameters such as logs
            level = myCof.getValue('log', 'level')
            # Set the log format,% (actime) s represents the time,% (name)s represents the incoming identification name,% (levelname)s represents the log level,% (message)s represents the log message
            format = '%(asctime)s - %(name)s-%(levelname)s: %(message)s'
            # Set basic level of log
            logging.basicConfig(level=level, format=format)
            # Initialize the log object. Hunwei is name
            mylog = logging.getLogger('Hunwei')
            # Splice log directory
            log_path = os.path.join(LOGDIR, 'testReport')
            # Generate a file handle. filename is the file path. The when table is the time. D indicates days. There are up to 15 log files in the backuCount=15 directory. Encoding='utf-8'log character format
            fh = TimedRotatingFileHandler(filename=log_path, when="D", backupCount=15, encoding='utf-8')
            # Set the format of the history log file name, and the corresponding log will be automatically generated on a certain day
            fh.suffix = "%Y-%m-%d.log"
            # Set the log level of file output
            fh.setLevel(level)
            # Set the log format for file output
            fh.setFormatter(logging.Formatter("%(asctime)s - %(name)s-%(levelname)s: %(message)s"))
            # Add file handle to log object
            mylog.addHandler(fh)
            #Return logging object
            return mylog
    
    #Mode code
    mylog = Log.getMylog()
    # mylog.debug('debug')
    # mylog.info('info')
    # mylog.warn('warm')
    # mylog.error('error')
    # mylog.fatal('fatal')

     

    6, Mail module - sendemail py

    1. Open the SMTP service of the mailbox

    email providers can start smtp services. If you know what smtp is and know the settings, you can skip this paragraph

    Take qq email as an example

    Enter qqmail Com log in to the mailbox, find [settings] - [account], and click POP3/SMTP to open it (the mark in the figure below is wrong, don't be misled)

    Send text messages as described

    After opening, we will get a key and keep it well,

     

     

    2. Learn to use the SMTP lib library to send emails with attachments

    Add mail parameters to profile

    host is the mail server, and Tencent's is SMTP qq. com,  

    Port is the mail server port. When smtp is enabled, Tencent will notify the port number by email

    user is the mailbox and pwd is the key obtained when opening smtp

                   from_addr is the sending email address, which is the same as user

                   to_addr is the inbox, which can be separated by commas

    Connect to smtp server

    Generally, exceptions will be thrown if you fail to connect to any server, so use try to create an image of SMTP lib, bring the server and port, and then connect with the user name and password

    Send mail

    Before sending email, you need to build an email content, so the email library can be downloaded through pip install email and used

    First build a plain text content, so import MIMEText,

    The following is a screenshot of the successful construction and sending of the email content. msg message is the email content, which requires parameters such as text, sender, recipient and email subject

    After sending successfully, enter the mailbox to view the mail

    After the mail is sent successfully, you have learned the basics. There are two mail types, MIMEMultipart multimedia inner type and MIMEApplication attachment inner type. I won't elaborate. You can see the encapsulation code later

                   

    3. Packaging code

    Before encapsulating the code, I know that the general automation report is in html format. Here I take the previous test and put it in the project report directory for easy use, as shown in the following figure

    After encapsulating the mail module, two lines of code can send the test report.

               

                    sendEmail. Snippet of PY

     

    import os
    import smtplib
    from common.getConfig import myCof
    from email.mime.text import MIMEText  #Import plain text format
    from email.mime.multipart import MIMEMultipart
    from email.mime.application import MIMEApplication
    from common.initPath import REPORTDIR
    
    # host = myCof.getValue('email', 'host')
    # port = int(myCof.getValue('email', 'port'))
    # user = myCof.getValue('email', 'user')
    # pwd = myCof.getValue('email', 'pwd')
    # to_addr = myCof.getValue('email', 'to_addr')
    # #Defines a plain text message, From defines the sender, To defines the recipient, and Subject defines the message title
    # msg = MIMEText('hello,send by python_test...','plain','utf-8')
    # msg['From'] = user
    # msg['To'] = to_addr
    # msg['Subject '] =' test mail sending '
    # try:
    #     #Connect the smtp service dialog and create an object
    #     smtp = smtplib.SMTP_SSL(host=host, port=port)
    #     #logon server
    #     smtp.login(user=user, password=pwd)
    #     # Send mail
    #     smtp.sendmail(from_addr=user, to_addrs=to_addr, msg=msg.as_string())
    #     # End conversation with server
    #     smtp.quit()
    #     print('email sent successfully ')
    # except Exception as e:
    #     print('failed to send mail, reason: {} '. format(str(e)))
    
    class SendMail(object):
    
        def __init__(self):
            """
            Initialization file path and related configuration
            """
            all_path = []
            #Get the name of the report file in the test report directory
            for maindir, subdir, file_list in os.walk(REPORTDIR):
                pass
    
            #Splice file absolute path
            for filename in file_list:
                all_path.append(os.path.join(REPORTDIR, filename))
            self.filename = all_path[0]
            self.host = myCof.get('email', 'host')
            self.port = myCof.get('email', 'port')
            self.user = myCof.get('email', 'user')
            self.pwd = myCof.get('email', 'pwd')
            self.from_addr = myCof.get('email', 'from_addr')
            self.to_addr = myCof.get('email', 'to_addr')
    
    
    
        def get_email_host_smtp(self):
            """
            connect stmp The server
            :return:
            """
            try:
                self.smtp = smtplib.SMTP_SSL(host=self.host, port=self.port)
                self.smtp.login(user=self.user, password=self.pwd)
                return True, 'Connection successful'
            except Exception as e:
                return False, 'Failed to connect to mailbox server, reason:' + str(e)
    
    
        def made_msg(self):
            """
            Build an email
            :return:
            """
            # Add a multi-component mail
            self.msg = MIMEMultipart()
    
            with open(self.filename, 'rb') as f:
                content = f.read()
            # Create text content
            text_msg = MIMEText(content, _subtype='html', _charset='utf8')
            # Add to multi-component messages
            self.msg.attach(text_msg)
            # Create attachments to messages
            report_file = MIMEApplication(content)
            report_file.add_header('Content-Disposition', 'attachment', filename=str.split(self.filename, '\\').pop())
    
            self.msg.attach(report_file)
            # theme
            self.msg['subject'] = 'Automated test report'
            # Sender
            self.msg['From'] = self.from_addr
            # addressee
            self.msg['To'] = self.to_addr
    
    
        def send_email(self):
            """
            Send mail
            :return:
            """
            isOK, result = self.get_email_host_smtp()
            if isOK:
                self.made_msg()
                self.smtp.send_message(self.msg, from_addr=self.from_addr, to_addrs=self.to_addr)
            else:
                return isOK, result
    
    
    # abc = SendMail()
    # abc.send_email()
    
    

     

    7, Message module - sendmsg py

    1. Create enterprise wechat application

    Establish an application on enterprise wechat to add relevant personnel as the carrier of receiving messages

    The enterprise wechat application token is realized in Python,

    Create a company on the official website of enterprise wechat, or use the company's enterprise wechat signal to obtain the enterprise ID of enterprise wechat

    Create application

     

    Get AgentId and Secret

    Enter the new application details page to get these two fields

    1. Learn to send enterprise wechat messages

    Senior developers will directly find the API development guide of enterprise wechat. They should know how to package it.

    First, configure the three parameters that should be obtained when creating to basecon INI file

                     

    Get the token of enterprise wechat

    The API url of the obtained token is spliced, and the fields of corpid and corpsecret are used as parameters, using requests The get () method requests to parse the token field value from the result set

    Send message

    The code for sending a message is shown in the following figure. First splice the url of the api sending the message, add the token value obtained above, and then construct a message, convert it into a message in bytes format, and use requests Post send message

    Enterprise wechat received the message, as shown in the figure below

    2. Code encapsulation

    After encapsulation, you only need two lines of code to send the message

               sendMsg. Snippet of PY

    import requests
    import json
    from common.getConfig import myCof
    
    # # Get the parameters of enterprise wechat
    # corpid = myCof.get('wechat', 'corpid')
    # corpsecret = myCof.get('wechat', 'corpsecret')
    # agentid = myCof.get('wechat', 'agentid')
    # # API for splicing and obtaining token
    # url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=' + corpid + '&corpsecret=' + corpsecret
    # # Use the requests request API and convert it to JSON format
    # response = requests.get(url)
    # res = response.json()
    # #Get token print
    # token = res['access_token']
    # print(token)
    # # Splicing api for sending messages
    # url = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=' + token
    # # Build a message JSON string
    # jsonmsg = {
    #                "touser" : "@all",
    #                "msgtype" : "text",
    #                "agentid" : agentid,
    #                "text" : {
    #                    "Content": "API interfaces from scratch"
    #                },
    #                "safe":0
    #             }
    # # Convert JSON into str and then into bytes
    # data = (bytes(json.dumps(jsonmsg), 'utf-8'))
    # # Send messages using requests post
    # requests.post(url, data, verify=False)
    
    class SendMsg(object):
        def __init__(self):
            self.corpid = myCof.get('wechat', 'corpid')
            self.corpsecret = myCof.get('wechat', 'corpsecret')
            self.agentid = myCof.get('wechat', 'agentid')
    
    
        def getToken(self):
            if self.corpid is None or self.corpsecret is None:
                return False, 'Enterprise wechat related information is not configured'
            url = 'https://qyapi.weixin.qq.com/cgi-bin/gettoken?corpid=' + self.corpid + '&corpsecret=' + self.corpsecret
            response = requests.get(url)
            res = response.json()
            self.token = res['access_token']
            return True, 'Enterprise wechat token Get success'
    
        def sendMsg(self, msg):
            _isOK, result = self.getToken()
            if _isOK:
                url = 'https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=' + self.token
                jsonmsg = {
                       "touser" : "@all",
                       "msgtype" : "text",
                       "agentid" : self.agentid,
                       "text" : {
                           "content" : msg
                       },
                       "safe":0
                    }
                data = (bytes(json.dumps(jsonmsg), 'utf-8'))
                requests.post(url, data, verify=False)
            else:
                print(result)
    
    
    
    
    # wechatMsg = SendMsg()
    # wechatMsg.sendMsg('API interface from scratch ')

     

    8, Variable parameterization module - parameterization py

    The first seven sections have finished the periphery of the automated test framework, and then we will start to write the core modules.

    There are many variables that can be configured for automated test tools, which are divided into two categories: system environment variables (artificially configured variables before execution, which still exist after execution), and temporary variables (variables parameterized during execution, which disappear after execution and wait for next parameterization)

    1. System environment variables

    This is very simple. We can write it directly in the configuration file. For example, we need an account and password and set it as the system environment variable directly in basccon Add in ini

                    

    Then we import myCon and use the getValue method to get the parameters of the variable,

                    

    This is the first step. We need to design the format of variables, such as {aaa}, ${aaa}. The former uses {} to identify that aaa needs parameterization, while the latter uses ${} to identify that aaa needs parameterization,

    Then define ${} as the identifier of the variable

    Take variable name

    So how do we get the variable name? For example, ${phone}, you can't get the mobile phone number directly. You need to get the phone. At this point, we can use the regular expression library, re to deal with it

    Regular expressions are very powerful and have many rules. I won't repeat them here. re is the basic library of Python, so you can search the tutorial on the rookie station. These are the two methods we need to use

         

          

    The code and execution results are shown in the figure below

                   

    Parameterize variables

    Just add the acquisition of configuration parameters to the above code. The code and execution results are shown in the figure below: the variables have been parameterized.

     

    2. Temporary variables

    Temporary variable, which is a job to test the basis of Python code, object-oriented, attribute, setattr, getattr and other knowledge points. If you understand, we only need to add a few lines of code to it.

    First define an empty class. Instead of attributes and methods, use setattr to add attributes and values to this empty class during code execution. This attribute is a temporary variable. If you want to call it, you can use getattr to get the value of the attribute and parameterize it

    The code and execution results are as follows

                  

        

    Parameterization of temporary variables

    After setting the temporary variable, the parameterization process is similar to that of the system environment variable. The difference is that Mycon Change getValue ('par ', key) to getattr(Paramte, key)

    The code and execution results are as follows:

    3. Code encapsulation

                    parameteriZation. Snippet of PY

    import re
    from common.getConfig import myCof
    
    # phone = myCof.getValue('par','phone')
    # print(phone)
    # pwd = myCof.getValue('par', 'pwd')
    # print(pwd)
    #Define a string with two variables
    # data = '{PHONE : ${phone}, PASSWORD: ${pwd}}'
    # #Define regular matching rules
    # ru = r'\${(.*?)}'
    # #Cyclic variable name
    # while re.search(ru, data):
    #     #Take the first variable
    #     res = re.search(ru, data)
    #     #Fetch name
    #     key = res.group(1)
    #     #Fetch environment variable
    #     value = myCof.getValue('par', key)
    #     #Replace variable
    #     data = re.sub(ru, value, data, 1)
    # #Print replaced string
    # print(data)
    
    # Empty class for temporary variable
    # class Paramete():
    #     pass
    # # Set temporary variables
    # setattr(Paramete, 'phone', '15381819299')
    # setattr(Paramete, 'pwd', '654321')
    # # Direct call value printing
    # # print('direct print: '+ parameter() phone)
    # # Print via getattr
    # # print('getattr print: '+ getattr (parameter,' phone '))
    # data = '{PHONE : ${phone}, PASSWORD: ${pwd}}'
    # #Define regular matching rules
    # ru = r'\${(.*?)}'
    # #Cyclic variable name
    # while re.search(ru, data):
    #     #Take the first variable
    #     res = re.search(ru, data)
    #     #Fetch name
    #     key = res.group(1)
    #     #Fetch environment variables
    #     value = getattr(Paramete, key)
    #     #Replace variable
    #     data = re.sub(ru, value, data, 1)
    #
    # print(data)
    
    class Paramete:
        pass
    
    def replace_data(data):
        """
        Replace variable
        :param data:
        :return:
        """
        ru = r'\${(.*?)}'
        while re.search(ru, data):
            res = re.search(ru, data)
            item = res.group()
            keys = res.group(1)
    
            # Find the system environment variable first, and replace it if any; If not, find the temporary variable
            try:
                value = myCof.get('test_data', keys)
            except Exception as e:
                value = getattr(Paramete, keys).encode('utf-8').decode('unicode_escape')
            finally:
                data = re.sub(ru, value, data, 1)
        return data
    
    def analyzing_param(param):
        """
         ${abc}take out abc
        :param param:
        :return:
        """
        ru = r'\${(.*?)}'
        if re.search(ru, param):
            return re.findall(ru, param)[0]
        return param
    
    # print(replace_data('${phone}, ${pwd}'))

     

    9, API request module - sendapirequests py

    1. Download requests Library

    Third party libraries, so you need to use the command: pip install requests # to download

    2. requests Library Learning

    Request type of requests

    The common request types are shown in the figure below

                

    At present, the main requests are get and post

                 requests.get(url = 'URL of request api', params = 'parameters of get request, which can be null', headers = 'request header, which can be null if the interface is not verified')

                 requests.post(url = 'URL of request api', json = 'if json parameter, use json field', data = 'if form format, use data parameter', files = 'if data is file, use file parameter', headers = 'request header, if interface is not verified, it can be empty')

    post can pass json, data and file parameters, but only one of them can be passed.

     

    3. api request encapsulation

                sendApirequest.py code snippet

    This file is under the kemel directory

    class SendApirequests(object):
    
        def __init__(self):
            self.session = requests.session()
    
        def request_Obj(self, method, url, params=None, data=None, json=None, files=None, headers=None,):
            opetype = str.lower(method)
            if opetype == 'get':
                response = requests.get(url=url, params=params, headers=headers)
            elif opetype == 'post':
                response = requests.post(url=url, json=json, data=data, files=files, headers=headers)
            return response

    Encapsulated code call - get

     

    Encapsulated code call - post

     

    10, Module of public method (core) - commkeyword py

    1. Public method concept

    A hierarchical concept of software MCV

    M is the bottom layer and model layer. The code encapsulated in front is this kind of module, which belongs to the bottom code.

    C is the middle layer and control layer, which encapsulates all the underlying access methods.

    V is the high-level, session layer, and the interface operation is also provided to users.

    Differentiation of MVC (layered concept)

    All modules in the common directory are M

    The module in kemel directory is C

    The analysis case and operation file are V

    The public method is mainly the operation entrance of sub packaging all the underlying modules, and provides some special public methods

    What is a special public method?

    For example, automatically generate mobile phone numbers, automatically generate ID number, automatically generate random strings, automatically generate area codes throughout the country, set variables, split fields, get the specified fields in the string, and so on.

    2. Packaging

    Encapsulation completes the public method, which will be used in combination with later factories

               commKeyword. Snippet of PY

                

    import json
    import jsonpath
    import datetime
    from common.getConfig import myCof
    from common.getCase import Getcase
    from common.operatorDB import OpeartorDB
    from common.parameteriZation import Paramete, analyzing_param, replace_data
    from common.sendApirequest import SendApirequests
    from common.sendEmail import SendMail
    from common.sendMsg import SendMsg
    
    
    class CommKeyword(object):
        def __init__(self):
            self.operatordb = OpeartorDB()
            self.getCase = Getcase()
            self.sendApi = SendApirequests()
            self.sendMail = SendMail()
            self.sedMsg = SendMsg()
    
    
        def get_exceut_case(self, **kwargs):
            """
            Get the current execution case
            :return: bl, cases Whether the first parameter returns success or not, and the second parameter is the use case or failure reason
            """
            try:
                cases = self.getCase.read_all_excels()
            except Exception as e:
                return False, 'Failed to get use case, reason:' + str(e)
            return True, cases
    
        def get_current_casefile_name(self, **kwargs):
            """
            Get the name of the execution case file
            :return: Return case file name
            """
            try:
                fileName = myCof.getValue('case', 'testcase')
            except Exception as e:
                return False, 'Case file name is not set in parameter, please check the configuration file'
            return True, fileName
    
        def send_api(self, **kwargs):
            """
            Send use case request post, get
            :param kwargs:  Requested parameters, including url,headers,data etc.
            :return:  bl, cases One parameter returns whether it is successful or not, and two parameters request result or failure reason
            """
            try:
                url = replace_data(kwargs['url'])
                method = kwargs['method']
                if kwargs['headers'] is None:
                    headers = None
                else:
                    _isOk, result = self.format_headers(replace_data(kwargs['headers']))
                    if _isOk:
                        headers = result
                    else:
                        return _isOk, result
    
                if kwargs['data'] is not None:
                    try:
                        jsondata = json.loads(replace_data(kwargs['data']))
                        data = None
                    except ValueError:
                        data = replace_data(kwargs['data'])
                        jsondata = None
                else:
                    data = None
                    jsondata = None
                response = self.sendApi.request_Obj(method=method, url=url, json=jsondata, data=data, headers=headers)
            except Exception as e:
                return False, 'Send request failed' + str(e)
            return True, response
    
    
    
    
    
        def set_sheet_dict(self):
            """
            :return: excl In the file sheet Page information
            """
            xlsx = Getcase(myCof.get('excel', 'casename'))
            sh_dict = xlsx.sheet_count()
            setattr(Paramete, 'sheetdict', sh_dict)
            sheetdict = getattr(Paramete, 'sheetdict')
            return sheetdict
    
    
        def set_common_param(self, key, value):
            """
            :param key:  Public variable name
            :param value: parameter
            :return:
            """
            setattr(Paramete, key, value)
    
        def get_commom_param(self, key):
            """
            :param key: Public variable name
            :return: Take variable value
            """
            return getattr(Paramete, key)
    
    
        def get_current_sheet_name(self):
            """
            :return: Returns the of the current execution case sheet Page name
            """
            sh_index = self.get_commom_param('sheetindex')
            sh_dict = self.get_commom_param('sheetdict')
            for sh in sh_dict:
                if sh.title().find(str(sh_index)) != -1:
                    sheet_name = sh_dict[sh.title().lower()]
            return sheet_name
    
        def get_json_value_as_key(self, *args, **kwargs):
            """
            obtain json in key Corresponding value,Stored variable param
            The default parameters are:
            result:Variables used to receive results
            method:Method called, with or without ${    } All right
            param_x:Parameter, unlimited quantity. The format can be ${    }Will be replaced with existing data
            """
            try:
                param = kwargs['result']
                jsonstr = kwargs['param_1']
                key = kwargs['param_2']
            except KeyError:
                return False, 'Method is missing parameters and execution failed'
    
            param = analyzing_param(param)
            jsonstr = replace_data(jsonstr)
            key = replace_data(key)
    
            if param is None or jsonstr is None or key is None:
                return False, 'The passed in parameter is null, execution failed'
            try:
                result = json.loads(jsonstr)
            except Exception:
                return False, 'The incoming dictionary parameter is in the wrong format, and the execution failed'
            key = '$..' + key
            try:
                value = str(jsonpath.jsonpath(result, key)[0])
            except Exception:
                return False, 'In the dictionary[' + jsonstr + ']No key[' + key + '], Execution failed'
            setattr(Paramete, param, value)
            return True, ' Already achieved[' + value + ']==>[${' + param + '}]'
    
    
    
        def format_headers(self, param):
            """
            Format request header
            :param param:excel Read it out in the header,From browser f12 Inside direct copy of
            :return:
            """
            if param is None:
                return False, 'Headers Empty'
            list_header = param.split('\n')
            headers = {}
    
            for li in list_header:
                buff = li.split(':')
                try:
                    headers[buff[0]] = buff[1]
                except IndexError:
                    return False, 'Headers Wrong format'
            return True, headers
    
    
        def set_variable(self, **kwargs):
            """
            set variable
            :param kwargs:
            :return:
            """
            try:
                var = kwargs['result']
                param = kwargs['param_1']
            except KeyError:
                return False, 'Method is missing parameters and execution failed'
            if var is None or param is None:
                return False, 'The passed in parameter is null, execution failed'
            setattr(Paramete, var, param)
            return True, ' Variable already set[' + param + ']==>[${' + var + '}]'
    
    
        def execut_sql(self, **kwargs):
            """
            implement SQL
            :param kwargs:
            :return:
            """
            try:
                sql = kwargs['param_1']
            except KeyError:
                return False, 'Method is missing parameters and execution failed'
            try:
                var = kwargs['result']
                par = kwargs['param_2']
            except Exception:
                var = None
            isOK, result = self.operatordb.excetSql(sql)
            if isOK and var is not None:
                data = result[par]
                setattr(Paramete, var, data)
                return True, 'implement SQL:[' + sql + ']Success, success' + par + 'Data[' + data + ']==>[${' + var + '}]'
            elif isOK and var is None:
                return True, 'implement SQL:[' + sql + ']success'
            elif isOK is False:
                return isOK, result
    
    
        def send_email(self):
            """
            Send mail
            :return:
            """
            return self.sendMail.send_email()
    
    
        def send_msg(self, **kwargs):
            """
            send message
            :param kwargs:
            :return:
            """
            title = kwargs['title']
            url = kwargs['url']
            code = kwargs['code']
            result = kwargs['result'].encode('utf-8').decode('unicode_escape')
            nowTime = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')  # Now?
            msg = nowTime + '\n Case name:' + title + '\n Request:' + url + '\n Response code:' + code + '\n Response information:' + result
            self.sedMsg.sendMsg(msg)
    
    
    # header = 'Access-Control-Allow-Credentials: true\nAccess-Control-Allow-Origin: http://test-hcz-static.pingan.com.cn\naccessToken: 8b1f056249134c4f9fb7b573b25ce08c'
    # _isOK, headers = format_headers(header)
    # print(headers, type(headers))
    

                

     

    11, Factory packaging (core) - methodfactory py

    1. Factory logic

    A factory is a public method invocation entry.

    Someone passes in the key text, and the factory will find the public method corresponding to the key text, execute the method to get the result, and finally return it to the person calling the factory.

    How can we find a way through words?

    The configuration file is needed. In the configuration file, we configure the public methods encapsulated in the previous section one by one,

    Format: text = method name

    As shown in the figure below:

                 

    Code implementation: first get the name of the public method through the configuration file, then use the getattr method to find the public method on the public module object, and then execute the method to get the desired result.

    As shown in the figure below

                 

    If it is executed in a use case, write it in this format

    Print results and report display

     

    The effect can only be seen after the following closure and use case analysis are written. Come on

     

     

    2. Packaging

                

                methodFactory. Snippet of PY

    from common.getConfig import myCof
    from kemel.commKeyword import CommKeyword
    
    # #Initialize common method module
    # comKey = CommKeyword()
    # #Get all public method configuration parameters
    # comKW = dict(myCof.items('commkey'))
    # #Get case method name
    # method_Name = comKW ['get current execution case']
    # #Get the corresponding module of the public method module through getattr
    # func = getattr(comKey, method_Name, None)
    # #Execute the public method obtained above to get the use case
    # cases = func(aa=None)
    # #Print use case
    # print(cases)
    
    class MethodFactory(object):
    
        def __init__(self):
            self.comKey = CommKeyword()
            self.comKW = dict(myCof.items('commkey'))
    
    
        def method_factory(self, **kwargs):
            """
            Use case common method factory
            The default parameters are:
            result:The variable used to receive the result. The format can be ${abc}
            method:The methods called here are designed in Chinese
            param_x:Parameter, unlimited quantity. The format can be ${abc}Will be replaced with existing data
            """
    
            if kwargs.__len__() > 0:
                try:
                    kwargs['method']
                except KeyError:
                    return False, 'keyword:Use case[method]No field method parameter is empty.'
                try:
                    method = self.comKW[str(kwargs['method']).lower()]
                except KeyError:
                    return False, 'keyword:method[' + kwargs['method'] + '] non-existent,Or not configured.'
            else:
                return False, 'No reference'
            try:
                func = getattr(self.comKey, method, None)
                _isOk, reselt = func(**kwargs)
                return _isOk, reselt
            except Exception as e:
                return False, 'keyword:Execution failed. It is estimated that it does not exist. Exception:' + str(e)
    
    
    
    
    
    # fac = MethodFactory()
    # print(fac.method_factory(method = 'get current use case file name'))
    
    
    
    
    

     

     

    12, Analyze use case (core) - testcase py

    1. Detailed explanation

    python automatically a unit testing framework unittest, which is excellent for automatic testing.

    First quote a theory:

    --------------------------------------------

    Students who have done automated testing should all know the unittest framework in python. It is a set of testing framework built in python and is relatively easy to learn. The four core concepts of unittest framework are:

    Test case: it is our test case. Unittest provides a basic class TestCase, which can be used to create new test cases. An instance of TestCase is a test case; Test case methods in unittest start with test, and the execution order will be sorted according to the ASCII value of the method name.

    Test fixture: test fixture, which is used to build and destroy the test case environment. That is, the SetUp of the preparation environment before the test of the use case (SetUp precondition) and the restoration of the environment after the test (TearDown postcondition). For example, the environment required by the test case is to log in and obtain the token before the test. After running, the environment needs to be restored before executing the next use case to avoid affecting the test results of the next use case.

    Test suite: test suite, which is used to put the test cases that need to be executed together into one piece for execution, which is equivalent to a basket. We can use TestLoader to load test cases into the test suite.

    test runner: used to execute test cases and return the execution results of test cases. It can also use graphical or text interfaces to show the returned test results more vividly, such as HTML testrunner.

    --------------------------------------------

    Automated test process: it is based on the TestCase + ddt data mode in unittest to form an automated use case set (commonly known as data-driven). Then, the test suite in unittest will centrally manage the use cases, and finally use the test runner in unittest to execute the centralized use cases and generate test reports

    Analyze use cases. This is the data-driven section. It has been encapsulated in testcase Py, you can learn from the code and comments by yourself

     

    2. Packaging

                testCase.py code snippet

    import unittest
    import json
    from library.ddt import ddt, data
    from common.log import mylog
    from kemel.methodFactory import MethodFactory
    
    isOK = True
    e = Exception()
    
    @ddt      #Reference data driven decorator
    class TestCase(unittest.TestCase):
        metFac = MethodFactory()     #Initialize factory class
        isOK, cases = metFac.method_factory(method='Get the current execution case')
        isOK, fileName = metFac.method_factory(method='Get the name of the current case file')
        if isOK is False:
            mylog.error('Failed to get use case')
            quit()
        
        #Call factory public method entry
        def _opear_keyword(self, **kwargs):    
            return self.metFac.method_factory(**kwargs)
    
        #assert methods 
        def _assert_res_expr(self, rules, reponse, expr):
            """
            assert methods 
            :param rules:Result contains, result equals, result status
            :param res:
            :param expr:
            :return:
            """
            try:
                res = reponse.json()
            except Exception:
                res = reponse.text
    
            headers = reponse.headers
            code = str(reponse.status_code)
            _reason = 'success'
            if rules == 'Results include':
                if type(expr) is str:
                    res = json.dumps(res, ensure_ascii=False)
                    print_result = json.dumps(json.loads(res), sort_keys=True, indent=2, ensure_ascii=False)
                else:
                    print_result = res
                try:
                    self.assertIn(expr, res)
                except AssertionError as e:
                    _isOk = False
                    _reason = 'result:\n[' + print_result + ']\n  Check value not included:\n  [' + expr + ']'
                else:
                    _isOk = True
                    _reason = 'result:\n[' + print_result + ']\n  Including check value:\n  [' + expr + ']'
            elif rules == 'The result is equal to':
                if type(expr) is str:
                    res = json.dumps(res, ensure_ascii=False)
                    print_result = json.dumps(json.loads(res), sort_keys=True, indent=2, ensure_ascii=False)
                else:
                    print_result = res
                try:
                    self.assertEqual(expr, res)
                except AssertionError as e:
                    _isOk = False
                    _reason = 'result:\n[' + res + ']\n  Not equal to check value:\n  [' + expr + ']'
                else:
                    _isOk = True
                    _reason = 'result:\n[' + res + ']\n  Equal to check value:\n  [' + expr + ']'
            elif rules == 'Result status':
                try:
                    self.assertEqual(expr, code)
                except AssertionError as e:
                    _isOk = False
                    _reason = 'result:\n[' + code + ']\n  Not equal to check value:\n  [' + expr + ']'
                else:
                    _isOk = True
                    _reason = 'result:\n[' + code + ']\n  Equal to check value:\n  [' + expr + ']'
            elif rules == 'Head contains':
                if type(expr) is str:
                    headers = json.dumps(headers, ensure_ascii=False)
                    print_header = json.dumps(json.loads(headers), sort_keys=True, indent=2, ensure_ascii=False)
                else:
                    print_header = headers
                try:
                    self.assertIn(expr, headers)
                except AssertionError as e:
                    _isOk = False
                    _reason = 'Head result:\n[' + print_header + ']\n  Check value not included:\n [' + expr + ']'
                else:
                    _isOk = True
                    _reason = 'Head result:\n[' + print_header + ']\n  Including check value:\n [' + expr + ']'
            elif rules == 'Head equals':
                if type(expr) is str:
                    headers = json.dumps(headers, ensure_ascii=False)
                    print_header = json.dumps(json.loads(headers), sort_keys=True, indent=2, ensure_ascii=False)
                else:
                    print_header = headers
                try:
                    self.assertEqual(expr, headers)
                except AssertionError as e:
                    _isOk = False
                    _reason = 'Head result:\n[' + print_header + ']\n  Not equal to check value:\n  [' + expr + ']'
                else:
                    _isOk = True
                    _reason = 'Head result:\n[' + print_header + ']\n  Equal to check value:\n  [' + expr + ']'
            return _isOk, _reason
    
        
        #Print the use case information and execution results. Because they are TestCase, they will be displayed in the test report. Therefore, design the output format to make the report Meida
        def postPinrt(self, **case):
            if case['interface'] is not None:
                print('\n------------------------------------------------------------------\n')
            print('Interface:[' + case['interface'] + ']')
            if case['method'] is not None:
                print('Type:[' + case['method'] + ']')
            if case['data'] is not None:
                print('Parameters:[' + case['data'] + ']')
            if 'get' == str.lower(case['method']):
                if '?' in str(case['url']):
                    url = str(case['url'])
                    a, param = url.split('?')
                    if param is not None:
                        print('Parameters:[')
                        datalist = str(param).split('&')
                        for data in datalist:
                            print(data)
                        print(']')
                else:
                    print('[[without parameters]')
            print('\n------------------------------------------------------------------\n')
    
    
    
    
        @data(*cases)     #, a data-driven decorator, which extracts the elements in the use case list and passes the elements to test_ In the case of audit
        def test_audit(self, case):
            _isOk = True
            #If the interface is commfun, the corresponding public method will be called
            if case['interface'] == 'commfun':
                """
                If the interface is a public method, the fields are as follows
                method: Public method name
                title: Return to receive fruit
                url: parameter
                data: parameter ...Four parameters temporarily
                """
                _isOk, _strLog = self._opear_keyword(method=case['method'],
                                                     result=case['title'],
                                                     param_1=case['url'],
                                                     param_2=case['headers'],
                                                     param_3=case['data'],
                                                     param_4=case['validaterules'])
            else:
                rows = case['case_id'] + 1
                title = case['title']
                expect = str(case['expected'])
                #Send the request. If the interface in the use case file is not equal to commfun and the method is post or get, it will be executed
                _isOK, result = self.metFac.method_factory(**case)
                if _isOk:
                    response = result
                    code = str(response.status_code)
                    try:
                        res = json.dumps(response.json())
                        self.metFac.method_factory(method='set variable', result='response', param_1=response)    #Return json save
                    except ValueError:
                        res = response.text
                        self.metFac.method_factory(method='set variable', result='response', param_1=res)     #Return html or xml, txt save
    
                    if case['validaterules'] is None:
                        _isOk = True
                        _strLog = 'Use case[' + str(case['case_id']) + ']: [' + title + ']Execution complete.'
                    else:
                        rules = case['validaterules']
                        _isOk, _reason = self._assert_res_expr(rules, response, expect)
                        if _isOk:
                            _strLog = 'Use case[' + str(case['case_id']) + ']: [' + title + ']Implementation passed. \n Verification results:\n' + _reason
                        else:
                            _strLog = "Use case[" + str(case['case_id']) + ']: [' + title + ']Execution failed.\n reason:\n' + _reason
                            #Error reporting interface to send information to enterprise wechat
                            self.metFac.method_factory(title=title, method='send message', api=case['interface'], url=case['url'], code=code, result=res)
                else:
                    _strLog = "Use case[" + str(case['case_id']) + ']: [' + title + ']Execution failed. \n reason:\ n' + result
    
            if _isOk:
                mylog.info(_strLog)
                print(_strLog)
                self.postPinrt(**case)
    
            else:
                mylog.error(_strLog)
                print(_strLog)
                self.postPinrt(**case)
                raise
    
    
    
    
    

     

    13, The last run file - testrun py

    1. Code encapsulation

                 testRun.py code snippet

    Write the code of this file, and then read the use case module - getcase The rules in "py" design test cases, then put them in the data file directory, configure the name of the use case file in the configuration file, and then execute the automatic test

    import unittest
    import os
    from common.initPath import CASEDIR, REPORTDIR
    from kemel.methodFactory import MethodFactory
    from library.HTMLTestRunnerNew import HTMLTestRunner
    
    
    
    class TestRun(object):
        metFac = MethodFactory()
        def __init__(self):
            self.suit = unittest.TestSuite()
            load = unittest.TestLoader()
            self.suit.addTest(load.discover(CASEDIR))
    
            self.runner = HTMLTestRunner(
                stream=open(os.path.join(REPORTDIR, 'report.html'), 'wb'),
                title='Interface automation test report',
                description='Instead of manual smoking and manual regression, do more accurate tests',
                tester='HunWei'
            )
    
        def excute(self):
            self.runner.run(self.suit)
            self.metFac.method_factory(method='Send mail')
    
    
    
    if __name__=='__main__':
        run = TestRun()
        run.excute()
    # from kemel.methodFactory import MethodFactory
    # abc = MethodFactory()
    # isOK, cases = abc.method_factory(method = 'get current execution case')
    # print(cases)
    
    

    The test report after execution is as follows:

     

     

       

     

Posted by bestrong on Mon, 18 Apr 2022 16:22:08 +0930