RESTful-API接口python自动化测试:增查改删
一般来说,增删改查的测试可以作为一个流程,那么删除就要放到最后测,因此称为增查改删。
以数据集的增查改删除为例简单了解一下业务,通过数据库建表来了解我们需要的操作。
Mongodb的python mongoengine ORM建表
1 # -*- coding: utf-8 -*- 2 3 import datetime 4 from flask_mongoengine import MongoEngine 5 from mongoengine import signals 6 import helper 7 8 db = MongoEngine() 9 10 11 class ProductAttribute(db.EmbeddedDocument): 12 k = db.StringField(required=True, max_length=50) 13 v = db.StringField(required=True, max_length=100) 14 15 meta = { 16 'indexes': [ 17 { 18 'fields': ['k', 'v'], 19 'unique': True 20 } 21 ] 22 } 23 24 25 class Dataset(db.Document): 26 name = db.StringField(required=True, max_length=50) 27 type = db.StringField(max_length=20) 28 x_consumer_custom_id = db.StringField() 29 30 description = db.StringField(max_length=500) 31 status = db.StringField(required=True, max_length=50, default="created") 32 33 created_at = db.DateTimeField(required=True, default=datetime.datetime.utcnow) 34 updated_at = db.DateTimeField(required=True) 35 deleted_at = db.DateTimeField(default=None) 36 37 meta = { 38 'indexes': [ 39 { 40 'fields': ['name', 'x_consumer_custom_id'], 41 'unique': True 42 } 43 ] 44 } 45 46 def to_dict(self): 47 return helper.mongo_to_dict(self, []) 48 49 50 class ImageItem(db.Document): 51 image_id = db.StringField(required=True, max_length=50) 52 uri = db.URLField() 53 title = db.StringField(required=True, max_length=50) 54 55 description = db.StringField(max_length=500) 56 status = db.StringField(required=True, max_length=50, default="created") 57 58 created_at = db.DateTimeField(required=True, default=datetime.datetime.utcnow) 59 updated_at = db.DateTimeField(required=True) 60 61 meta = { 62 'indexes': [ 63 { 64 'fields': ['image_id'], 65 'unique': True 66 } 67 ] 68 } 69 70 def to_dict(self): 71 return helper.mongo_to_dict(self, []) 72 73 74 class ProductItem(db.Document): 75 product_id = db.StringField(required=True, max_length=50) 76 name = db.StringField(required=True, max_length=100) 77 category = db.StringField(required=True, max_length=50) 78 79 description = db.StringField(max_length=500) 80 status = db.StringField(required=True, max_length=50, default="created") 81 shelf = db.StringField(required=True, max_length=50, default="on") 82 83 images = db.ListField(db.URLField()) 84 85 attributes = db.EmbeddedDocumentListField(ProductAttribute) 86 87 created_at = db.DateTimeField(required=True, default=datetime.datetime.utcnow) 88 updated_at = db.DateTimeField(required=True) 89 90 meta = { 91 'indexes': [ 92 { 93 'fields': ['product_id'], 94 'unique': True 95 } 96 ] 97 } 98 99 def to_dict(self): 100 return helper.mongo_to_dict(self, []) 101 102 103 def update_timestamp(sender, document, **kwargs): 104 document.updated_at = datetime.datetime.utcnow() 105 106 signals.pre_save.connect(update_timestamp, sender=Dataset) 107 signals.pre_save.connect(update_timestamp, sender=ProductItem) 108 signals.pre_save.connect(update_timestamp, sender=ImageItem)
简单工程目录如下:
一 配置文件settings.py
1 # coding=utf-8 2 3 # HOST_PORT = "192.168.1.142:80" 4 # HOST_PORT = "http://127.0.0.1:5000" 5 # HOST_PORT = "http://47.92.xxx.7:80" 6 HOST_PORT = "http://127.0.0.1:80" 7 X_CONSUMER_CUSTOM_ID = "victor100" 8 9 DATASET_NAME = "dataset100" 10 PRODUCT_ID = "product108" 11 12 QUERY_PREFIX = "dataset" 13 PRODUCT_QUERY_PREFIX = "dataset" 14 15 # 要先把这几个产品创建了 16 PRODUCT_IDS = ['product100', 'product101', 'product102'] 17 SHELF = 'on' 18 19 20 CONTENT_TYPE = "application/json" 21 REPORT_FILE_NAME = "dataset_report.txt" 22 23 24 # 数据集增查改删 25 # 增 26 POST_URL = HOST_PORT + '/datasets' 27 # 查 28 GET_URL = POST_URL + '/{dataset_name}'.format(dataset_name=DATASET_NAME) 29 # 改 30 PATCH_URL = GET_URL 31 # 删 32 DELETE_URL = GET_URL 33 # 列表查 34 GET_LIST_URL = POST_URL 35 36 # 商品集增查改删 37 # 增 38 POST_PRODUCT_URL = HOST_PORT + '/datasets' + '/{dataset_name}/items'.format(dataset_name=DATASET_NAME) 39 # 查 40 GET_PRODUCT_URL = POST_PRODUCT_URL + '/{item_id}'.format(item_id=PRODUCT_ID) 41 # 改 42 PATCH_PRODUCT_URL = GET_PRODUCT_URL 43 PATCH_PRODUCTS_URL = POST_PRODUCT_URL 44 # 删 45 DELETE_PRODUCT_URL = GET_PRODUCT_URL 46 # 列表查 47 GET_LIST_PRODUCT_URL = POST_PRODUCT_URL
二 增加一个数据集
1 # coding=utf-8 2 import requests 3 import json 4 import sys 5 from datasets_api_test.config.settings import X_CONSUMER_CUSTOM_ID, CONTENT_TYPE, DATASET_NAME, POST_URL 6 from datasets_api_test.report.utils import generate_report 7 8 stage = """创建一个数据集""" 9 10 try: 11 headers = {} 12 headers["x-consumer-custom-id"] = X_CONSUMER_CUSTOM_ID 13 headers["Content-Type"] = CONTENT_TYPE 14 15 payload = {} 16 payload['dataset'] = {} 17 payload['dataset']['name'] = DATASET_NAME 18 payload['dataset']['type'] = "product" 19 payload['dataset']['description'] = "description001" 20 print(payload) 21 22 resp = requests.post(url=POST_URL, json=payload, headers=headers) 23 print(resp.status_code) 24 print(resp.content) 25 except Exception as e: 26 print(e) 27 28 29 generate_report(stage, POST_URL, headers, resp.status_code, resp.content, data=payload)
三 查询一个数据集
# coding=utf-8 import requests import json import time from datasets_api_test.config.settings import X_CONSUMER_CUSTOM_ID, GET_URL from datasets_api_test.report.utils import generate_report stage = """查询一个数据集""" try: headers = {} headers['x-consumer-custom-id'] = X_CONSUMER_CUSTOM_ID resp = requests.get(url=GET_URL, headers=headers) print(resp.status_code) print(resp.content) except Exception as e: print(e) generate_report(stage, GET_URL, headers, resp.status_code, resp.content, data=None)
四 修改一个数据集
# coding=utf-8 import requests import json from datasets_api_test.config.settings import X_CONSUMER_CUSTOM_ID, CONTENT_TYPE, DATASET_NAME, PATCH_URL from datasets_api_test.report.utils import generate_report stage = """修改一个数据集""" try: headers = {} headers['x-consumer-custom-id'] = X_CONSUMER_CUSTOM_ID para = {} para['dataset'] = {} para['dataset']['name'] = DATASET_NAME para['dataset']['description'] = 'description00000001' print(para) resp = requests.patch(url=PATCH_URL, json=para, headers=headers) print(resp.status_code) print(resp.content) except Exception as e: print(e) generate_report(stage, PATCH_URL, headers, resp.status_code, resp.content, data=para)
五 查询一个数据集列表
# coding=utf-8 import requests import json import time from datasets_api_test.config.settings import X_CONSUMER_CUSTOM_ID, GET_LIST_URL, QUERY_PREFIX from datasets_api_test.report.utils import generate_report stage = """查询一个数据集list列表""" try: headers = {} headers['x-consumer-custom-id'] = X_CONSUMER_CUSTOM_ID para = {} para['query'] = QUERY_PREFIX para['page'] = 1 para['per_page'] = 20 para['order'] = 'desc' para['state'] = 'created' resp = requests.get(url=GET_LIST_URL, params=para, headers=headers) print(resp.status_code) print(resp.content) except Exception as e: print(e) generate_report(stage, GET_LIST_URL, headers, resp.status_code, resp.content, data=para)
六 删除一个数据集
# coding=utf-8 import requests import json from datasets_api_test.config.settings import X_CONSUMER_CUSTOM_ID, DELETE_URL from datasets_api_test.report.utils import generate_report stage = """删除一个数据集""" try: headers = {} headers['x-consumer-custom-id'] = X_CONSUMER_CUSTOM_ID resp = requests.delete(url=DELETE_URL, headers=headers) print(resp.status_code) print(resp.content) except Exception as e: print(e) generate_report(stage, DELETE_URL, headers, resp.status_code, resp.content, data=None)
七 程序主入口
上面的结果我都写入了一个简单报告:
1 # coding=utf-8 2 import os 3 from datasets_api_test.config.settings import REPORT_FILE_NAME 4 5 def generate_report(stage, URL, headers, status_code, content, data=None): 6 file_dir = os.path.dirname(os.path.abspath(__file__)) 7 file_name = REPORT_FILE_NAME 8 file_path = os.path.join(file_dir, file_name) 9 print(file_path) 10 with open(file_path, 'a+') as f: 11 f.write('--------------------------------------\n') 12 f.write(stage + '\n') 13 f.write('请求部分' + '\n') 14 f.write(URL + '\n') 15 f.write(str(headers) + '\n') 16 f.write(str(data) + '\n') 17 f.write('\n') 18 f.write('响应部分' + '\n') 19 f.write(str(status_code) + '\n') 20 f.write(content) 21 f.write('--------------------------------------\n') 22 23 24 def generate_shell(path_list): 25 file_dir = os.path.dirname(os.path.abspath(__file__)) 26 file_name = "start.sh" 27 file_path = os.path.join(file_dir, file_name) 28 print(file_path) 29 with open(file_path, 'w+') as f: 30 f.write("#!/bin/bash" + '\n') 31 for line in path_list: 32 f.write("python" + " " + line + '\n') 33 34 return file_path
下面是主程序
# coding=utf-8 import os import sys import subprocess base_path = os.path.dirname(os.path.abspath(__file__)) sys.path.insert(0,base_path) print(sys.path) from datasets_api_test.report.utils import generate_shell if __name__ == '__main__': dataset_base = os.path.join(base_path, 'datasets') product_base = os.path.join(base_path, 'product_item') dataset_list = ['post_datasets_item.py', 'get_datasets_item.py', 'patch_datasets_item.py', 'get_datasets_list.py'] product_list = ['post_product_item.py', 'get_product_item.py', 'patch_product_item.py', 'get_product_list.py',] dp_list = ['delete_product_item.py'] dd_list = ['delete_datasets_item.py'] dataset_list_new = [os.path.join(dataset_base, i) for i in dataset_list] product_list_new = [os.path.join(product_base, i) for i in product_list] dp_list_new = [os.path.join(product_base, i) for i in dp_list] dd_list_new = [os.path.join(dataset_base, i) for i in dd_list] dataset_list_new.extend(product_list_new) # 如果不要删除新生的测试数据库的数据集和商品集表,就注释掉下面2行 dataset_list_new.extend(dp_list_new) dataset_list_new.extend(dd_list_new) print(dataset_list_new) shell_file = generate_shell(dataset_list_new) os.system('sh %s'%(shell_file))
这只是一个自己写的简单的测试小框架,但是应该用更加规范的写法来自动化测试。
八 自动化的接口测试
利用requests来进行接口测试 + unittest进行用例管理 + HTMLTextRunner自动生成html测试报告 + smtp进行邮件发送 + Jenkins进行持续集成
直接集成到test_process.py
# coding=utf-8 from __future__ import print_function import unittest import requests from HTMLTestRunner import HTMLTestRunner from config.settings import X_CONSUMER_CUSTOM_ID, DATASET_NAME, PRODUCT_ID, CONTENT_TYPE, REPORT_FILE_NAME, \ POST_URL, GET_URL, PATCH_URL, DELETE_URL, GET_LIST_URL, POST_PRODUCT_URL, GET_PRODUCT_URL, PATCH_PRODUCT_URL, \ DELETE_PRODUCT_URL, GET_LIST_PRODUCT_URL, QUERY_PREFIX from core.utils import generate_report from log.logger import logger class DatasetsAPITest(unittest.TestCase): def tearDown(self): logger.info('tear down...') def setUp(self): logger.info('tear...up') def test_post_datasets_item(self): stage = """创建一个数据集""" try: headers = {} headers["x-consumer-custom-id"] = X_CONSUMER_CUSTOM_ID headers["Content-Type"] = CONTENT_TYPE payload = {} payload['dataset'] = {} payload['dataset']['name'] = DATASET_NAME payload['dataset']['type'] = "product" payload['dataset']['description'] = "description001" resp = requests.post(url=POST_URL, json=payload, headers=headers) logger.info(resp.status_code) logger.info(resp.content) except Exception as e: logger.error(e) generate_report(stage, POST_URL, headers, resp.status_code, resp.content, data=payload) self.assertEquals(10, 10) def test_get_datasets_item(self): stage = """查询一个数据集""" try: headers = {} headers['x-consumer-custom-id'] = X_CONSUMER_CUSTOM_ID resp = requests.get(url=GET_URL, headers=headers) logger.info(resp.status_code) logger.info(resp.content) except Exception as e: logger.error(e) generate_report(stage, GET_URL, headers, resp.status_code, resp.content, data=None) def test_patch_datasets_item(self): stage = """修改一个数据集""" try: headers = {} headers['x-consumer-custom-id'] = X_CONSUMER_CUSTOM_ID para = {} para['dataset'] = {} para['dataset']['name'] = DATASET_NAME para['dataset']['description'] = 'description00000001' logger.info(para) resp = requests.patch(url=PATCH_URL, json=para, headers=headers) logger.info(resp.status_code) logger.info(resp.content) except Exception as e: logger.error(e) generate_report(stage, PATCH_URL, headers, resp.status_code, resp.content, data=para) def test_get_datasets_list(self): stage = """查询一个数据集list列表""" try: headers = {} headers['x-consumer-custom-id'] = X_CONSUMER_CUSTOM_ID para = {} para['query'] = QUERY_PREFIX para['page'] = 1 para['per_page'] = 20 para['order'] = 'desc' para['state'] = 'created' resp = requests.get(url=GET_LIST_URL, params=para, headers=headers) logger.info(resp.status_code) logger.info(resp.content) except Exception as e: logger.error(e) generate_report(stage, GET_LIST_URL, headers, resp.status_code, resp.content, data=para) def test_post_product_item(self): stage = """创建一个商品集""" try: headers = {} headers["x-consumer-custom-id"] = X_CONSUMER_CUSTOM_ID headers["Content-Type"] = CONTENT_TYPE payload = {} payload['item'] = {} payload['item']['product_id'] = PRODUCT_ID payload['item']['name'] = DATASET_NAME payload['item']['description'] = "description0000000000" payload['item']['category'] = "category0000000" # payload['attributes'] = [dict(k="hello", v="world"), dict(k="hello1", v="world1")] payload['item']['attributes'] = [{'k': "hello", 'v': 'world'}, {'k': "hello1", 'v': 'world2'}] payload['item']['images'] = ['http://www.baidu.com/hello', 'http://www.baidu.com/world'] logger.info(payload) resp = requests.post(url=POST_PRODUCT_URL, json=payload, headers=headers) logger.info(resp.status_code) logger.info(resp.content) except Exception as e: logger.error(e) generate_report(stage, POST_PRODUCT_URL, headers, resp.status_code, resp.content, payload) def test_get_product_item(self): stage = """查询一个商品集""" try: headers = {} headers['x-consumer-custom-id'] = X_CONSUMER_CUSTOM_ID resp = requests.get(url=GET_PRODUCT_URL, headers=headers) logger.info(resp.status_code) logger.info(resp.content) except Exception as e: logger.error(e) generate_report(stage, GET_PRODUCT_URL, headers, resp.status_code, resp.content, data=None) def test_patch_product_item(self): stage = """修改一个商品集""" try: headers = {} headers["x-consumer-custom-id"] = X_CONSUMER_CUSTOM_ID headers["Content-Type"] = CONTENT_TYPE payload = {} payload['item'] = {} payload['item']['product_id'] = PRODUCT_ID payload['item']['name'] = DATASET_NAME payload['item']['description'] = "ssss" payload['item']['category'] = "cate003" payload['item']['attributes'] = [{"k": "hello", "v": "world"}] payload['item']['images'] = ['http://www.baidu.com/hello', 'http://www.baidu.com/world'] logger.info(payload) resp = requests.patch(url=PATCH_PRODUCT_URL, json=payload, headers=headers) logger.info(resp.status_code) logger.info(resp.content) except Exception as e: logger.error(e) generate_report(stage, PATCH_PRODUCT_URL, headers, resp.status_code, resp.content, payload) def test_get_product_list(self): stage = """查询一个商品集列表""" try: headers = {} headers['x-consumer-custom-id'] = X_CONSUMER_CUSTOM_ID para = {} para['page'] = 1 para['per_page'] = 20 para['order'] = 'desc' para['status'] = 'created' resp = requests.get(url=GET_LIST_PRODUCT_URL, params=para, headers=headers) logger.info(resp.status_code) logger.info(resp.content) except Exception as e: logger.error(e) generate_report(stage, GET_LIST_PRODUCT_URL, headers, resp.status_code, resp.content, data=para) def test_delete_product_item(self): stage = """删除一个商品集""" try: headers = {} headers['x-consumer-custom-id'] = X_CONSUMER_CUSTOM_ID resp = requests.delete(url=DELETE_PRODUCT_URL, headers=headers) logger.info(resp.status_code) logger.info(resp.content) except Exception as e: logger.error(e) generate_report(stage, DELETE_PRODUCT_URL, headers, resp.status_code, resp.content, data=None) def test_delete_datasets_item(self): stage = """删除一个数据集""" try: headers = {} headers['x-consumer-custom-id'] = X_CONSUMER_CUSTOM_ID resp = requests.delete(url=DELETE_URL, headers=headers) logger.info(resp.status_code) logger.info(resp.content) except Exception as e: logger.error(e) generate_report(stage, DELETE_URL, headers, resp.status_code, resp.content, data=None)
而进行的主函数就是:
# coding=utf-8 from __future__ import absolute_import import unittest from core.test_process import DatasetsAPITest from HTMLTestRunner import HTMLTestRunner if __name__ == '__main__': # 打乱了我的流程顺序 # test_dir = './core/' # discover = unittest.defaultTestLoader.discover(test_dir, pattern='test_*.py') func_list = [DatasetsAPITest('test_post_datasets_item'), DatasetsAPITest('test_get_datasets_item'), DatasetsAPITest('test_patch_datasets_item'), DatasetsAPITest('test_get_datasets_list'), DatasetsAPITest('test_post_product_item'), DatasetsAPITest('test_get_product_item'), DatasetsAPITest('test_patch_product_item'), DatasetsAPITest('test_get_product_list'), DatasetsAPITest('test_delete_product_item'), DatasetsAPITest('test_delete_datasets_item') ] my_test_suite = unittest.TestSuite() my_test_suite.addTests(func_list) fp = file('dataset_report.html', 'wb') runner = HTMLTestRunner.HTMLTestRunner( stream=fp, title='Datasets API Test', description='This demonstrates the report output by HTMLTestRunner.' ) runner.run(my_test_suite)
这些自动化测试完全可以集成到CI中去。