Interface automation framework (Pytest+request+Allure)

Posted by AShain on Mon, 21 Feb 2022 08:02:18 +0100

preface:

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

For framework upgrade, please move to: Interface automation framework 2 - upgraded version (Pytest+request+Allure)

1, Brief introduction

Environment: Mac, Python 3, Pytest, Allure, Request

pytest==3.6.0
pytest-allure-adaptor==1.7.10(Discard)
pytest-rerunfailures==5.0
configparser==3.5.0
PyYAML==3.12
requests==2.18.4
simplejson==3.16.0
----------------------------------------
2020-4-30 to update
pytest==5.3.1
allure-pytest==2.8.6
allure-python-commons==2.8.6
⚠️Note: pytest-allure-adaptor Deprecated, replaced by allure-pytest;
install allure-pytest If necessary, the pytest-allure-adaptor uninstall

Process: read Yaml test data - generate test cases - execute test cases - generate Allure Report
Design description of module class:

Request.py encapsulates the request method and supports multi protocol extension (get\post\put)
Config.py reads configuration files, including configurations of different environments and email related configurations
Log.py encapsulates log recording methods, which are divided into debug, info, warning, error and critical
Email.py encapsulates the smtplib method and sends an email notification of the running result
Assert.py encapsulation 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:

Code structure png

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

Framework process jpg

3, Detailed functions and instructions

1. Define configuration file config ini

The file distinguishes between the test environment [private_debug] and the formal environment [online_release] and defines relevant configuration items respectively, and the [mail] part is 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=wang@user.com&password=123456

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

[mail]
#Send email message
smtpserver = smtp.163.com
sender = test1@163.com
receiver = wang@user.com
username = wang@user.com
password = 123456

2. Package after reading yaml test data

See Section 1 for an example of yaml test data. One interface can define multiple case data, get_parameter refers to the encapsulated method of reading yaml data. After cyclic reading, multiple case data are 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:
    @allure.feature('Home')
    @allure.severity('blocker')
    @allure.story('Basic')
    def test_basic_01(self, action):
        """
            Use case description: viewing 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 whole framework run py

if __name__ == '__main__':
    # Define test set
    args = ['-s', '-q', '--alluredir', xml_report_path]
    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('Failed to execute the use case. Please check the environment configuration')
        raise

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

5,err.log instance

[ERROR 2018-08-24 09:55:37]Response body != expected_msg, expected_msg is {"error":"Please register or log in 9 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 code

    def assert_body(self, body, body_msg, expected_msg):
        """
        verification response body The value of any attribute in the
        :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):
        """
        verification response body Does the contain 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. The overview of Allure report is shown in the figure below:

Allure report png

2. See the following figure for Email:

Email.png

5, Subsequent optimization

1. Integrate Jenkins and generate Allure report using Jenkins plug-in
2. Automatic testing of multithreaded concurrent interface
3. Interface encryption

Open source code: https://github.com/wangxiaoxi3/API_Automation

The following is the supporting materials. For friends who do [software testing], it should be the most comprehensive and complete war preparation warehouse. This warehouse has also accompanied me through the most difficult journey. I hope it can also help you!

Finally, it can be in the official account: programmer Hao! Get a 216 page interview document of Software Test Engineer for free. And the corresponding video learning tutorials for free!, It includes basic knowledge, Linux essentials, Shell, Internet program principles, Mysql database, special topics of packet capture tools, interface test tools, test advanced Python programming, Web automation test, APP automation test, interface automation test, advanced continuous integration of test, test architecture, development test framework, performance test, security test, etc.

If my blog is helpful to you and you like my blog content, please click "like", "comment" and "collect" for three times! Friends who like software testing can join our testing technology exchange group: 779450660 (there are various software testing resources and technical discussions)

Topics: Python unit testing software testing