Python + request + unittest implementation interface test framework integration instance, pythonunittest

Source: Internet
Author: User

Python + request + unittest implementation interface test framework integration instance, pythonunittest

1. Why do I need to write code to automate interfaces?

We all know that many interface testing tools can test interfaces, such as postman, jmeter, and fiddler, and are easy to use. Why do I need to write code to automate interfaces? Although the tool is convenient, there are also shortcomings:

Test data is not controllable

In essence, interface testing is a test of data. It calls an interface and inputs some data. Then, the interface returns some data. Verify the correctness of the data returned by the API. You have to manually insert test data into the database before running the test case using tools. In this way, our interface test is not so "Automated ".

Unable to test the encryption Interface

This is a major flaw in the interface test tool. For example, the interface tool we developed earlier has no problem in testing, but an interface that needs to encrypt/decrypt the number of interface parameters is encountered, for example, md5, base64, AES, and other common encryption methods. This chapter introduces encrypted interfaces in Chapter 1. Or the interface parameters need to use timestamps, which is also difficult to simulate by tools.

Insufficient scalability

While enjoying the convenience brought by tools, we are often subject to the limitations brought by tools. For example, if I want to generate a test report in HMTL format, I want to send the test report to the specified mailbox. I want to perform a scheduled task for interface testing. I want to perform continuous integration for interface testing. These requirements are difficult to implement by tools.

2. Automated Interface Test Design

The Interface Test Call process can be summarized and a test database is added.

AverageInterface toolsTest process:

1. The interface tool calls the interface of the tested system (passing parameter username = "zhangsan ").

2. The system interface transmits parameters (username = "zhangsan")Formal Database.

3. Assemble the query results into data in a certain format and return them to the caller.

4. Check the correctness of the Interface Test manually or through the asserted function of the tool.

To make the API test data controllable, the test procedure is as follows:

1. Interface Test Project firstTest DatabaseInsert test data (personal information of zhangsan ).

2. Call the tested system interface (passing the parameter username = "zhangsan ").

3. The system interface queries the test database based on the parameter (username = "zhangsan") and obtains the personal information of zhangsan.

4. Assemble the query results into data in a certain format and return them to the caller.

5. data returned by the asserted interface through the unit test framework (personal information of zhangsan) and a test report is generated.

We recommend that you use an independentTest Database.

2. requests Library

Requests uses urllib3, so it inherits all its features. Requests supportHTTP connection persistence and Connection Pool, SupportedUse cookies to maintain sessions, SupportedFile Upload, SupportedAutomatically determines the encoding of the response content.For more details about the request library, see my previous article on interface testing basics:

Http://www.bkjia.com/article/122571.htm? Pc

Http://www.bkjia.com/article/108168.htm

3. Sample Code for interface Testing

The following example usesPython + djangoThe developed user sign-in system is used as the background to display the Interface Test code.

Why Develop interfaces? Who is the main use of the developed interface?

Frontend and backend separation is a development trend in Web application development in recent years. This mode will bring the following advantages:

1. the backend does not need to be proficient in front-end technology (HTML, JavaScript, and CSS). It only focuses on data processing and provides APIs.

2. The front-end is becoming more and more professional. You can obtain data through APIS to focus on page design.

3. The frontend and backend separation adds the application scope of interfaces. The developed interfaces can be applied to Web pages or mobile apps.

In this development mode, interface Testing becomes especially important.

Sample Code of the developed interface:

# Add the press conference interface to implement def add_event (request): eid = request. POST. get ('eid', '') # press conference id name = request. POST. get ('name', '') # press conference title limit = request. POST. get ('limit ', '') # Number of Restricted persons status = request. POST. get ('status', '') # status address = request. POST. get ('address', '') # address start_time = request. POST. get ('start _ time ','') # press conference time if eid = ''or name ='' or limit = ''or address ='' or start_time = '': return JsonResponse ({'status': 10021, 'message': 'parameter error'}) result = Event. objects. filter (id = eid) if result: return JsonResponse ({'status': 10022, 'message': 'event id already exists'}) result = event. objects. filter (name = name) if result: return JsonResponse ({'status': 10023, 'message': 'event name already exists'}) if status = '': status = 1 try: Event. objects. create (id = eid, name = name, limit = limit, address = address, status = int (status), start_time = start_time) handle T ValidationError: error = 'start _ time format error. it must be in YYYY-MM-DD HH: MM: SS format. 'Return JsonResponse ({'status': 10024, 'message': error}) return JsonResponse ({'status': 200, 'message': 'add event success '})

Parameters for receiving a press conference through a POST request: the press conference id, title, number of people, status, address, and time.

First, determine that fields such as eid, name, limit, address, and start_time cannot be empty. Otherwise, JsonResponse () returns the corresponding status code and prompt. JsonResponse () is a very useful method. It can directly convert the dictionary into Json format and return it to the client.

Next, determine whether the conference id exists and whether the name of the Conference exists. If yes, return the corresponding status code and prompt information.

Next, determine whether the press conference status is empty. If it is empty, set the status to 1 (True ).

Finally, insert the data to the Event table. If the date format is incorrect during the insertion process, a ValidationError error is thrown. The system receives the exception and returns the corresponding status and prompt. Otherwise, the insertion is successful, returns the status code 200 and "add event success" prompts.

# Def get_event_list (request): eid = request. GET. get ("eid", "") # press conference id name = request. GET. get ("name", "") # Press Conference name if eid = ''and name ='': return JsonResponse ({'status': 10021, 'message ': 'parameter error'}) if eid! = '': Event = {} try: result = Event. objects. get (id = eid) response t ObjectDoesNotExist: return JsonResponse ({'status': 10022, 'message': 'query result is empty'}) else: event ['eid'] = result. id event ['name'] = result. name event ['limit'] = result. limit event ['status'] = result. status event ['address'] = result. address event ['start _ time'] = result. start_time return JsonResponse ({'status': 200, 'message': 'suc Cess ', 'data': event}) if name! = '': Datas = [] results = Event. objects. filter (name _ contains = name) if results: for r in results: event ={} event ['eid'] = r. id event ['name'] = r. name event ['limit'] = r. limit event ['status'] = r. status event ['address'] = r. address event ['start _ time'] = r. start_time datas. append (event) return JsonResponse ({'status': 200, 'message': 'success', 'data': datas}) else: return JsonResponse ({'status ': 10022, 'message': 'query result is empty '})

The GET request is used to receive the id and name parameters of the Conference. Both parameters are optional. First, when the two parameters are empty at the same time, the interface returns status code 10021 with a parameter error.

If the press conference id is not blank, the id query is given priority. Because of the uniqueness of the id, only one query result is available, and the query result is key: the value pair is stored in the defined event dictionary, and the data dictionary is used as the value corresponding to the data in the entire returned dictionary.

The name query is a fuzzy query, and there may be more than one query data, and the returned data is slightly complicated. First, place each queried data in a dictionary event, put each dictionary into the array datas, and then return the entire array as the value corresponding to the data in the returned dictionary.

Interface Test code example

# Import requestsurl = "http: // 127.0.0.1: 8000/api/get_event_list/" r = requests. get (url, params = {'eid': '1'}) result = r. json () print (result) assert result ['status'] = 200 assert result ['message'] = "success" assert result ['data'] ['name'] = "xx product release "assert result ['data'] ['address'] =" Beijing Forest Park Water Cube "assert result ['data'] ['start _ time'] =" 2016-10-15T18: 00: 00"

Because the "launch query interface" is of the GET type, the first parameter is the URL address of the called interface and the parameter of the interface set by params, called through the get () method of the requests library, parameters are organized in a dictionary.

The json () method can convert the json format data returned by the interface into a dictionary.

The next step is to use the assert statement to dock the data in the dictionary for assertions. Asserted status, message, and data.

Use unittest unit test framework to develop Interface Test Cases

# Import unittestimport requestsclass GetEventListTest (unittest. testCase): def setUp (self): self. base_url = "http: // 127.0.0.1: 8000/api/get_event_list/" def test_get_event_list_eid_null (self): The '''eid parameter is null ''' r = requests. get (self. base_url, params = {'eid': ''}) result = r. json () self. assertEqual (result ['status'], 10021) self. assertEqual (result ['message'], 'parameter error') def test_get_event_list_eid_error (self): ''' eid = 901 the query result is null ''' r = requests. get (self. base_url, params = {'eid': 901}) result = r. json () self. assertEqual (result ['status'], 10022) self. assertEqual (result ['message'], 'query result is empty') def test_get_event_list_eid_success (self): ''' query result Based on eid is successful ''' r = requests. get (self. base_url, params = {'eid': 1}) result = r. json () self. assertEqual (result ['status'], 200) self. assertEqual (result ['message'], 'success') self. assertEqual (result ['data'] ['name'], u'mx6 Press Conference ') self. assertEqual (result ['data'] ['address'], u'beijing National Convention Center ') def test_get_event_list_nam_result_null (self ): ''' keyword 'abc' query ''' r = requests. get (self. base_url, params = {'name': 'abc'}) result = r. json () self. assertEqual (result ['status'], 10022) self. assertEqual (result ['message'], 'query result is empty') def test_get_event_list_name_find (self): ''' keyword 'Press Conference 'fuzzy query''' r = requests. get (self. base_url, params = {'name': 'conference '}) result = r. json () self. assertEqual (result ['status'], 200) self. assertEqual (result ['message'], 'success') self. assertEqual (result ['data'] [0] ['name'], u'mx6 Press Conference ') self. assertEqual (result ['data'] [0] ['address'], u'beijing National Convention Center ') 49if _ name _ = '_ main _': unittest. main ()

Unittest unit testing framework can helpOrganize and run interface test cases.

4. Implementation of an automated interface testing framework

Unittest has helped us do most of the work for automated interface testing.Integrated Database Operations, AndHTMLTestRunner Test Report GenerationExtension.

The framework structure is as follows:

Pyrequests framework:

Db_fixture/: Initialize the interface test data.

Interface/: used to compile interface automation test cases.

Report/: generate an API automation test report.

Db_config.ini: database configuration file.

HTMLTestRunner. py unittest extension of the unit test framework to generate a test report in HTML format.

Run_tests.py: Run all interface test cases.

4.1 Database Configuration

First, you must modify the system to direct the database to the test database. Take the MySQL database as an example.DjangoFor the project, modify the file ../guest/settings. py. You can create a test database in the system test environment. The purpose is to prevent data from being cleared or contaminated to the function test database. Similar to django projects, other framework development projects are generally completed by developers. Our testers are more concerned with the code of the testing framework.

4.2 framework code implementation

4.2.1. First, createCreate a database configuration file.../db_config.ini

4.2.2. Next,Simple encapsulation of database operations, database table data insertion and Removal,.../Db_fixture/Mysql_db.py

Import pymysql. cursorsimport osimport configparser as cparser #======= Reading db_config.ini setting =========== base_dir = str (OS. path. dirname (OS. path. dirname (_ file _) base_dir = base_dir.replace ('\', '/') file_path = base_dir + "/db_config.ini" cf = cparser. configParser () cf. read (file_path) host = cf. get ("mysqlconf", "host") port = cf. get ("mysqlconf", "port") db = cf. get ("mysqlconf", "db_name") user = cf. get ("mysqlconf", "user") password = cf. get ("mysqlconf", "password ") #======= MySql base operating ================================ class DB: def _ init _ (self): try: # Connect to the database self. connection = pymysql. connect (host = host, port = int (port), user = user, password = password, db = db, charset = 'utf8mb4', cursorclass = pymysql. cursors. dictCursor) Doesn't pymysql. err. operationalError as e: print ("Mysql Error % d: % s" % (e. args [0], e. args [1]) # clear table data def clear (self, table_name): # real_ SQL = "truncate table" + table_name + "; "real_ SQL =" delete from "+ table_name +"; "with self. connection. cursor () as cursor: cursor.exe cute ("SET FOREIGN_KEY_CHECKS = 0;") cursor.exe cute (real_ SQL) self. connection. commit () # insert SQL statement def insert (self, table_name, table_data): for key in table_data: table_data [key] = "'" + str (table_data [key]) + "'" key = ','. join (table_data.keys () value = ','. join (table_data.values () real_ SQL = "insert into" + table_name + "(" + key + ") VALUES (" + value + ")" # print (real_ SQL) with self. connection. cursor () as cursor: cursor.exe cute (real_ SQL) self. connection. commit () # close database def close (self): self. connection. close () # init data def init_data (self, datas): for table, data in datas. items (): self. clear (table) for d in data: self. insert (table, d) self. close () if _ name _ = '_ main _': db = DB () table_name = "sign_event" data = {'id': 1, 'name': 'redmi ', 'limit': 2000, 'status': 1, 'address': 'Beijing convention and exhibition Center', 'start _ time ': '2017-08-20 00:25:42 '} table_name2 = "sign_guest" data2 = {'realname': 'alen', 'phone': 2016, 'email ': 'alen @ mail.com ', 'sign': 0, 'event _ id': 1} db. clear (table_name) db. insert (table_name, data) db. close ()

First, read the db_config.ini configuration file. Create a DB class, __init _ () method, and connect to the database through pymysql. connect.

Because only the clearing and insertion of database tables are used here, only the clear () and insert () methods are created. The insert () method converts data insertion into a simple format and converts the dictionary into an SQL insert statement, which facilitates the creation of database table data.

Finally, the close () method is used to close the database connection.

4.2.3. NextCreate Test Data,.../Db_fixture/Test_data.py

Import syssys. path. append ('.. /db_fixture ') try: from mysql_db import db1_t ImportError: from. mysql_db import DB # create datadatas = {'sign _ event': [{'id': 1, 'name': 'redrice Pro release', ''limit '': 2000, 'status': 1, 'address': 'Beijing convention and exhibition Center', 'start _ time': '2017-08-20 14:00:00 '}, {'id': 2, 'name': 'number of participants is 0', ''limit': 0, 'status': 1, 'address': 'beijing' convention and exhibition Center ', 'Start _ time': '2017-08-20 14:00:00 '}, {'id': 3, 'name': 'Current status 0 closed ', ''limit '': 2000, 'status': 0, 'address': 'Beijing convention and exhibition Center', 'start _ time': '2017-08-20 14:00:00 '}, {'id': 4, 'name': 'Press conference terminated ', ''limit': 2000, 'status': 1, 'address ': 'Beijing convention and exhibition Center', 'start _ time': '2017-08-20 14:00:00 '}, {'id': 5, 'name': 'xiaomi 5 Press Conference ', ''limit '': 2000, 'status': 1, 'address': 'Beijing National Convention Center ', 'start _ time ': '2017-08-20 14:00:00 '},], 'sign _ guest': [{'id': 1, 'realname': 'alen', 'phone': 2017, 'email ': 'alen @ mail.com', 'sign': 0, 'event _ id': 1}, {'id': 2, 'realname ': 'Has sign', 'phone': 13511001101, 'email ': 'sign @ mail.com', 'sign': 1, 'event _ id': 1 }, {'id': 3, 'realname': 'Tom ', 'phone': 13511001102, 'email': 'Tom @ mail.com ', 'sign': 0, 'event _ id': 5},],} # Inster table datasdef init_data (): DB (). init_data (datas) if _ name _ = '_ main _': init_data ()

The init_data () function is used to read data in the datas dictionary, call the clear () method in the DB class to clear the database, and then call the insert () method to insert Table data.

4.2.4 writeInterface Test Cases. Create and add the press conference interface Test file.../interface/Add_event_test.py

Import unittestimport requestsimport OS, sysparentdir = OS. path. dirname (OS. path. dirname (OS. path. abspath (_ file _) sys. path. insert (0, parentdir) from db_fixture import test_dataclass AddEventTest (unittest. testCase): ''' Add a press conference ''' def setUp (self): self. base_url = "http: // 127.0.0.1: 8000/api/add_event/" def tearDown (self): print (self. result) def test_add_event_all_null (self): ''' all parameters are empty '''payload = {'eid': '','': '', 'limit ': '', 'address':" ", 'start _ time':''} r = requests. post (self. base_url, data = payload) self. result = r. json () self. assertEqual (self. result ['status'], 10021) self. assertEqual (self. result ['message'], 'parameter error') def test_add_event_eid_exist (self): ''' id already exists ''' payload = {'eid': 1, 'name ': 'One plus 4 release', 'limit': 2000, 'address': "Shenzhen bao", 'start _ time': '20160301'} r = requests. post (self. base_url, data = payload) self. result = r. json () self. assertEqual (self. result ['status'], 10022) self. assertEqual (self. result ['message'], 'event id already exists') def test_add_event_name_exist (self): ''' name already exists ''' payload = {'eid': 11, 'name': 'redrice Pro Press Conference ', 'limit': 2000, 'address': "Shenzhen ", 'start _ time': '000000'} r = requests. post (self. base_url, data = payload) self. result = r. json () self. assertEqual (self. result ['status'], 10023) self. assertEqual (self. result ['message'], 'event name already exists') def test_add_event_data_type_error (self): ''' Date Format error ''' payload = {'eid': 11, 'name': 'One-plus 4 cell phone launches ', 'limit': 2000, 'address': "Shenzhen bao", 'start _ time ': '20140901'} r = requests. post (self. base_url, data = payload) self. result = r. json () self. assertEqual (self. result ['status'], 10024) self. assertIn ('start _ time format error. ', self. result ['message']) def test_add_event_success (self): ''' added successfully ''' payload = {'eid': 11, 'name ': 'One plus 4 cell phone release', 'limit': 2000, 'address': "Shenzhen bao", 'start _ time ': '2017-05-10 12:00:00 '} r = requests. post (self. base_url, data = payload) self. result = r. json () self. assertEqual (self. result ['status'], 200) self. assertEqual (self. result ['message'], 'add event success ') if _ name _ =' _ main _ ': test_data.init_data () # initialize the Interface Test Data unittest. main ()

Before testing the interface, call the init_data () method in the test_data.py file to initialize the test data in the database.

The created AddEventTest test class inherits the unittest. TestCase class. It creates test cases, calls related interfaces, and verifies the data returned by the interfaces.

4.2.5. CreateRun_tests.pyFile

When a certain number of interfaces are developed, considerSub-file sub-directoryComeDivisionInterface Test Cases, how to Batch Execute use cases under different file directories? Provided by the unittest unit test frameworkDiscover ()Methods can help us achieve this. Use HTMLTestRunner extension to generate a test report in HTML format.

Import time, syssys. path. append ('. /interface') sys. path. append ('. /db_fixture ') from HTMLTestRunner import HTMLTestRunnerimport unittestfrom db_fixture import test_data # specify the test case as the interface directory test_dir =' in the current folder '. /interface 'discover = unittest. defaultTestLoader. discover (test_dir, pattern = '* _ test. py ') if _ name _ = "_ main _": test_data.init_data () # initialize the Interface Test Data now = time. strftime ("% Y-% m-% d % H _ % M _ % S") filename = '. /report/'+ now + '_result.html' fp = open (filename, 'wb') runner = HTMLTestRunner (stream = fp, title = 'guest Manage System Interface Test report ', description = 'Implementation Example with: ') runner. run (discover) fp. close ()

First, call the init_data () function in the test_data.py file to initialize the interface test data.

Use the discover () method provided by the unittest framework to find all test files matching * _ test. py in the interface/directory (* star matches any character ).

HTMLTestRunner is an extension of the unittest unit testing framework. It uses the HTMLTestRunner () class provided by HTMLTestRunner to replace the TextTestRunner () class of the unittest unit testing framework to generate a test report in HTML format.

Unfortunately, HTMLTestRunner does not support Python3.x. You can find the HTMLTestRunner. py file for Python3.x on the Internet and use it in your own interface automation project.

Obtain the current time using the strftime () method of time and convert it to a certain time format. Name of the test report. The purpose is to avoid overwrite the report because the name of the generated Report is the same. Finally, store the test report in the report/directory. For example, a complete API automation test report.

 

The above is all the content of this article. I hope it will be helpful for your learning and support for helping customers.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.