Interface automation framework (Pytest+request+Allure)

Foreword:

Interface automation refers to the automation at the interface level of the simulation program. Because the interface is not easy to change and the maintenance cost is lower, it is deeply loved by major companies.
Interface automation includes two parts, functional interface automation testing and concurrent interface automation testing.
This article focuses on the first, functional interface automation framework.

1. Brief introduction

Environment: Mac, Python 3, Pytest, Allure, Request
Process: Read Yaml test data – generate test cases – execute test cases – generate Allure report
Design instructions for module classes:

Request.py encapsulates the request method and can support multi-protocol extensions (get\post\put)


Config.py Read configuration files, including: configurations for different environments, email-related configurations


Log.py encapsulates the log method, which is divided into: debug, info, warning, error, critical


Email.py encapsulates the smtplib method and sends email notifications of the running results.


Assert.py encapsulates the assert method


run.py core code. Define and execute use case sets and generate reports

Yaml test data format is as follows:

---
Basic:
  dec: "Basic settings"
  parameters:
    -
      url: /settings/basic.json
      data: slug=da1677475c27
      header: {
                 "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko)\
                  Chrome/67.0.3396.99 Safari/537.36",
                 "Content-Type": "keep-alive"
              }

2. Code structure and framework process

1. The code structure is shown in the figure below:

2. The framework process is shown in the figure below:

3. Detailed functions and usage instructions

1. Define the configuration file config.ini

This file distinguishes between the test environment [private_debug] and the formal environment [online_release] and defines related configuration items respectively. The [mail] part is the mail-related configuration items.

# http interface test framework configuration information

[private_debug]
# debug test service
tester=your name
environment=debug
versionCode = your version
host=www.jianshu.com
loginHost = /Login
loginInfo = [email protected] & amp;password=123456

[online_release]
# release official service
tester=your name
environment=release
versionCode = v1.0
host=www.jianshu.com
loginHost = /Login
loginInfo = [email protected] & amp;password=123456

[mail]
#Send email message
smtpserver = smtp.163.com
sender = [email protected]
receiver = [email protected]
username = [email protected]
password = 123456

2. Read the yaml test data and encapsulate it

See the first section for an example of yaml test data. An interface can define multiple case data. get_parameter is an encapsulated method for reading yaml data. After reading in a loop, multiple case data will be stored in the list.

class Basic:
    params = get_parameter('Basic')
    url = []
    data = []
    header = []
    for i in range(0, len(params)):
        url.append(params[i]['url'])
        data.append(params[i]['data'])
        header.append(params[i]['header'])

3. Write use cases

class TestBasic:
    @pytest.allure.feature('Home')
    @allure.severity('blocker')
    @allure.story('Basic')
    def test_basic_01(self, action):
        """
            Use case description: View basic settings without logging in
        """
        conf = Config()
        data = Basic()
        test = Assert.Assertions()
        request = Request.Request(action)

        host = conf.host_debug
        req_url = 'http://' + host
        urls = data.url
        params = data.data
        headers = data.header

        api_url = req_url + urls[0]
        response = request.get_request(api_url, params[0], headers[0])

        assert test.assert_code(response['code'], 401)
        assert test.assert_body(response['body'], 'error', u'Please register or log in before continuing.')
        assert test.assert_time(response['time_consuming'], 400)
        Consts.RESULT_LIST.append('True')

4. Run the entire framework run.py

if __name__ == '__main__':
    # Define test set
    allure_list = '--allure_features=Home,Personal'
    args = ['-s', '-q', '--alluredir', xml_report_path, allure_list]
    log.info('Execution case set is: %s' % allure_list)
    self_args = sys.argv[1:]
    pytest.main(args)
    cmd = 'allure generate %s -o %s' % (xml_report_path, html_report_path)

    try:
        shell.invoke(cmd)
    except:
        log.error('Execution of the test case failed, please check the environment configuration')
        raise

    try:
        mail = Email.SendMail()
        mail.sendMail()
    except:
        log.error('Failed to send email, please check email configuration')
        raise

5. err.log example

[ERROR 2018-08-24 09:55:37]Response body != expected_msg, expected_msg is {"error":"Please register or log in before continuing."}, body is {"error":" Please register or log in before continuing."}
[ERROR 2018-08-24 10:00:11]Response time > expected_time, expected_time is 400, time is 482.745
[ERROR 2018-08-25 21:49:41]statusCode error, expected_code is 208, statusCode is 200

6. Assert part of the code

def assert_body(self, body, body_msg, expected_msg):
        """
        Verify the value of any attribute in the response body
        :param body:
        :param body_msg:
        :param expected_msg:
        :return:
        """
        try:
            msg = body[body_msg]
            assert msg == expected_msg
            return True

        except:
            self.log.error("Response body msg != expected_msg, expected_msg is %s, body_msg is %s" % (expected_msg, body_msg))
            Consts.RESULT_LIST.append('fail')

            raise

    def assert_in_text(self, body, expected_msg):
        """
        Verify that the response body contains the expected string
        :param body:
        :param expected_msg:
        :return:
        """
        try:
            text = json.dumps(body, ensure_ascii=False)
            # print(text)
            assert expected_msg in text
            return True

        except:
            self.log.error("Response body Does not contain expected_msg, expected_msg is %s" % expected_msg)
            Consts.RESULT_LIST.append('fail')

            raise

7. Request part code

def post_request(self, url, data, header):
        """
        Post request
        :param url:
        :param data:
        :param header:
        :return:

        """
        if not url.startswith('http://'):
            url = '%s%s' % ('http://', url)
            print(url)
        try:
            if data is None:
                response = self.get_session.post(url=url, headers=header)
            else:
                response = self.get_session.post(url=url, params=data, headers=header)

        except requests.RequestException as e:
            print('%s%s' % ('RequestException url: ', url))
            print(e)
            return()

        except Exception as e:
            print('%s%s' % ('Exception url: ', url))
            print(e)
            return()

        # time_consuming is the response time in milliseconds
        time_consuming = response.elapsed.microseconds/1000
        # time_total is the response time in seconds
        time_total = response.elapsed.total_seconds()

        Common.Consts.STRESS_LIST.append(time_consuming)

        response_dicts = dict()
        response_dicts['code'] = response.status_code
        try:
            response_dicts['body'] = response.json()
        except Exception as e:
            print(e)
            response_dicts['body'] = ''

        response_dicts['text'] = response.text
        response_dicts['time_consuming'] = time_consuming
        response_dicts['time_total'] = time_total

        return response_dicts

4. Allure report and email

1. Overview of Allure report, see the picture below:

2. Email is shown below:

5. Subsequent optimization

1. Integrate Jenkins and use the Jenkins plug-in to generate Allure reports
2. Multi-threaded concurrent interface automated testing
3. Interface encryption and parameter encryption

Finally, I would like to thank everyone who read my article carefully. Reciprocity is always necessary. Although it is not a very valuable thing, if you can use it, you can take it directly:

This information should be the most comprehensive and complete preparation warehouse for [software testing] friends. This warehouse has also accompanied tens of thousands of test engineers through the most difficult journey. I hope it can also help you!

The knowledge points of the article match the official knowledge archives, and you can further learn relevant knowledge. Cloud native entry-level skills treeInfrastructure automatic orchestration (Terraform)Introduction to Terraform 16876 people are learning the system