The author of httprunner is debugtalk. He is a senior developer. At present, there are five versions of httprunner
The usage of Httprunner is relatively simple and conforms to a variety of scenarios in work:
1. After completing the interface automation, it is required to continue the performance test of this interface (hrun has a built-in locust module, which can directly conduct pressure test)
2. There are too many interfaces to do, and only some parameters need to be replaced, and the work of interface automation will be completed (charles records and exports the. har file, and converts it into a. py file through the hrun command)
3. Every step of the interface response of the request library needs to add assertions, and hrun will automatically generate assertion methods
4. Third party libraries integrated by hrun: loguru and jmespath
Current version of hrun:
Environment preparation: install python, Httprunner, allure pytest:
pip install -i https://pypi.tuna.tsinghua.edu.cn/simple httprunner pip install -i https://pypi.tuna.tsinghua.edu.cn/simple allure-pytest
Environment verification, check the httprunner version:
httprunner -V
help:
httprunner --help
Generate scaffolding (quickly generate a hrun framework):
httprunner startproject PartsService
Convert the yml file in the scaffold into pytest file (execute in the root directory):
cd PartsService hmake testcases
Run httprunner and a single file (enter the root directory of the file to be run)
Mode 1: pytest [filename] Mode 2: hrun [filename] Mode 3: httprunner run [filename]
Run httprunner in batch to run all use cases under the file set
Mode 1: pytest [dirname] Mode 2: hrun [dirname] Mode 3: httprunner run [dirname]
see. har generate script help
har2case -h
The above is the basic usage of hrun, and the following is the secondary encapsulation of hrun
Allure: store the report result set of allure
api_data: store the request data of the interface (can be json file, xlsx file, yaml file)
api_testing: store API test cases
api_util: encapsulation method for API testing
common: public methods, such as reading database files
config: store global configuration files
databases: store database files
login: login
config file content:
[base] # the url domain you wants to use in UI testing backend_url =https://wwww.baidu.cn/ [report] ### pytest xxxx --alluredir ./report ### allure serve report ### hrun test_post_api.yml --alluredir=allure ### allure generate ./allure/ -o ./reports --clean
backend_url is the project address. If there are multiple sets of environments, you can put multiple URLs to switch
common file content:
import configparser import os class ConfigReader: def __init__(self, file_name): self.config = configparser.ConfigParser() config_path = os.path.split(os.path.realpath(__file__))[0] + os.sep + file_name self.config.read(config_path, "utf-8") def get_config(self, config_item): sections = self.config.sections() for section in sections: if self.config.has_option(section, config_item): return self.config.get(section, config_item) def get_config_by_section(self, config_item, section_name): return self.config.get(section_name, config_item)
Only one method of reading Config file is written here, which is convenient for switching multiple sets of database environments. It can be supplemented later if necessary
login file content:
from httprunner import HttpRunner, Config, Step, RunRequest, RunTestCase # todo: import httprunner module def get_base_url(): # todo: encapsulate project_host method, which is the global API path of the project from common import config_reader # todo: config_reader reference config_reader method return config_reader.ConfigReader('../config/base_config.cfg').get_config('backend_url') class TestCaseLogin(HttpRunner): # todo: define that the class name should start with Test config = Config("testcase description")\ .verify(False)\ # todo: ignore Https certificates .variables(**{ "Accept":"application/json, text/plain, */*", "Cookie":"SIAM_IMAGE_CODE=762410696575033344; SIAMTGT=TGT-892-4xraUmB3mrsA0MtWv11ENoZmq1t4CiBYgSGPJjG9NQrHpTqczc-SIAM; LtpaToken=AAECAzYxRjBCN0Y5NjFGMTYwQjlDTj1iaWVzb25nbGluL089Zm90b25UaDZzgyb50ZEjaw/jBYfTHXONKQ==", "userName": " xxxx", "password": "xxxx", "validCode": "xxx", "brandId":"x", }) # TODO: set global variables. The following Step methods can be used. Accept, Cookie, userName, password, validCode and brandId are set here teststeps = [ Step( RunRequest("Sign in") .post(get_base_url()+"API route") #todo: switch your own api path .with_cookies(**{"aaf875be9aad4feca52ccece8eade2df": "WyIzMTE4NjIwNDQ3Il0"}) # todo: API cookies .with_json({"userName": "$userName", "password": "$password", "validCode": "$validCode"}) # todo: reference the defined global variable .extract() .with_jmespath('body.data.token', 'token') # todo: get the response value from the response. The outermost layer is a fixed body .with_jmespath('headers.Server', 'cookies') # todo: take the token under the data of the body for the variable name later, so the body is specified earlier. Similarly, you can also take the data of the header (if necessary) .validate() .assert_equal("status_code", 200) .assert_equal('headers."Content-Type"', "application/json") .assert_equal("body.code", 200) .assert_equal("body.message", "success") ), ] if __name__ == "__main__": TestCaseLogin().test_start()
Write the login case to facilitate the subsequent hrun file call and realize login decoupling
api_util file contents:
import time from httprunner import HttpRunner,Config,Step,RunTestCase,RunRequest from login.get_token import TestCaseLogin as GetToken # todo: import login file def get_base_url(): from common import config_reader return config_reader.ConfigReader('../config/base_config.cfg').get_config('backend_url') class APIRequestConstructor(HttpRunner): # todo: create a basic class to clear and read the token information of hrun later config = ( Config("API Test execution") .base_url(get_base_url()) .verify(False)) teststeps = [ Step( RunTestCase("By login API obtain token") .call(GetToken) .export(*["token",]))] def reset_request_step(token=GetToken): # todo: encapsulates the method based on the class. If it is not passed by default, GetToken is used APIRequestConstructor.teststeps.clear() # todo: teardown, which belongs to the unit test framework, gives each use case a clean execution environment APIRequestConstructor.teststeps.append( # todo: add step action Step( RunTestCase("By login API obtain token") .call(token) .export(*["token",]) )) headers = { # todo: write header data according to the needs of the project "Host": "xxx", "Accept": "application/json, text/plain, */*", "Content-Type": "application/json;charset=utf-8", "brandId": "3", "Authorization": "${token}", "Cookie": "xxxx", } cookies = {"aaf875be9aad4feca52ccece8eade2df": "WyIxNjUxMTM1OTE1Il0"} # todo: write cookie data according to the needs of the project def create_run_request(request_name, url, request_json, body="body.data", headers=headers, cookies=cookies): # todo: encapsulate the use case steps and place the API name, request address and request parameters if request_json is not None: # todo: if the json format of the request parameter is not empty, execute the post request return RunRequest(request_name).post(url).with_headers(**headers).with_cookies(**cookies).with_json( request_json ).extract().with_jmespath(body, "res_data") else: # todo: if the request parameter json is empty, the post request will be executed return RunRequest(request_name).get(url).with_headers(**headers).with_cookies(**cookies) \ .extract().with_jmespath(body, "res_data") def contruct_request_step(run_request_obj): return Step( run_request_obj ) def perform_requests_and_get_last_response(steps): # todo: the execution step of putting into use case APIRequestConstructor.teststeps.extend(steps) # todo: steps to read teststeps obj = APIRequestConstructor() # todo: instantiating objects obj.test_start() time.sleep(1) res = obj.with_export(["res_data", "token"]).get_export_variables() return res
Secondary encapsulation of httprunner to reduce redundant code
Contents of databases file:
import copy import configparser import os class ConfigReader: def __init__(self, file_name): self.config = configparser.ConfigParser() config_path = os.path.split(os.path.realpath(__file__))[0] + os.sep + file_name self.config.read(config_path, "utf-8") def get_config(self, config_item): sections = self.config.sections() for section in sections: if self.config.has_option(section, config_item): return self.config.get(section, config_item) class DB(): def __init__(self, database_name): cf = ConfigReader("database_config_dev.cfg") # todo: which database is connected? If there are multiple environments, remember to switch the database configuration here self.host = cf.get_config(database_name + "_host") self.port = cf.get_config(database_name + "_port") self.user = cf.get_config(database_name + "_user") self.password = cf.get_config(database_name + "_password") self.database_name = database_name def connect(self): # todo: create database connection method import pymysql # Create database connection self.db = pymysql.connect(host=self.host, user=self.user, password= self.password, database=self.database_name) # Create cursor self.cursor = self.db.cursor() def get_one(self, sql): # todo: return a piece of data result = 0 try: self.connect() self.cursor.execute(sql) result = self.cursor.fetchone() self.close() except Exception as e: print('select error', e) return result def get_all(self, sql): # todo: return all qualified query results result = 0 try: self.connect() self.cursor.execute(sql) result = self.cursor.fetchall() self.close() except Exception as e: print("select error", e) return result def __edit(self, sql): # Create main function result = 1 # Set the result set for judgment when calling try: # Here is the try statement used to try the operation self.connect() self.cursor.execute(sql) self.db.commit() # Note that if you modify, delete, or add a database, you must commit. You do not need to commit to query or create tables self.close() except Exception as e: # If the operation fails, an operation exception is reported and the cursor is rolled back print('error :', e) result = 0 self.db.rollback() return result def insert(self, sql): # The following three insert statements are the same, but we look clearer when calling return self.__edit(sql) # Execute sql statements through the processing of the main function def delete(self, sql): # Delete statement return self.__edit(sql) def update(self, sql): # Modify statement return self.__edit(sql) def close(self): # Closing method self.cursor.close() # Close cursor self.db.close() # close database
For example, there are two sets of database environments here. It is easier to encapsulate them with this method
api_ Content of testing file:
import allure,pytest from loguru import logger from api_util.util import * @allure.feature("Parts information management query") class TestSearChParts(): @pytest.fixture(scope="class", autouse=True) @allure.feature("data dump") def teardown_(self): yield logger.debug("Implementation of the use case will end.") @allure.story("Branch: parts information management query") def test_Search_Parts_GetToken(self): reset_request_step() # todo: login file reference. GetToken is not passed by default # todo: create request method create_search_parts can be named by user. Call create_run_request method, passing in API name, API address and request body create_search_parts = \ create_run_request( "Accessory information management", "api/common/partsInfo/sparepart/v1/getSparePartByPage", {"status":1,"pageSize":10,"pageNum":1,"loading":"false"}) \ .with_jmespath("body.data.list[0].id","ids") \ .validate() \ .assert_equal("status_code", 200).assert_equal("body.message", "success") # todo: contruct_request_step is put into the execution step. If there are multiple steps, they need to be separated by commas create_out_of_warranty_payment_step = contruct_request_step( create_search_parts) # todo: perform_ requests_ and_ get_ last_ Put the response into the use case that needs to be executed. Similarly, if there are multiple, they need to be separated by commas res = perform_requests_and_get_last_response( [create_out_of_warranty_payment_step]) id = res['res_data']['list'][0]['id'] # todo: take out the response value of the interface response and get it to the dependent interface for use. Because of the encapsulated method, the external body of response is res_data, so you need to add print(id) @allure.story("Service station: parts information management query") def test_Search_Parts_Service_Station(self): reset_request_step(Service_Station) create_out_of_warranty_payment_run_request = \ create_run_request( "Accessory information management", "api/common/partsInfo/sparepart/v1/getSparePartByPage", {"status": 1, "pageSize": 10, "pageNum": 1, "loading": "false"}) \ .validate() \ .assert_equal("status_code", 200).assert_equal("body.message", "success") create_out_of_warranty_payment_step = contruct_request_step( create_out_of_warranty_payment_run_request) res = perform_requests_and_get_last_response( [create_out_of_warranty_payment_step])
Last run file method:
1. Right click the file to run
2. Click the green triangle on the left side of the method to execute a single use case
3. Run with command:
pytest [filename]
You can also use hrun [filename]
If you want to run all the use cases under the whole file set, you can directly pytest [dirname]
Locally generated allure Report
pytest xxxx --alluredir ./report # todo: xxx is the name of the hrun file or the name of the folder. Generate allure result set
allure serve report # todo: view the allure report locally (the console will give the url address)
allure generate ./report/ -o ./reports --clean # todo: clear the historical allure result set and add an allure report under the reports folder
jenkins configuring pytest+httprunner
1. To install the allure plug-in on jenkins:
Jenkins system management - global tool configuration - Allure Commandline add configuration
Check automatic installation, select from maven central, and select the latest version, which is currently 2.7
2. Configure job
New task: free style software project
2. Enter job configuration
Configure git
Repository URL: git address. Remember to add it after it git
Credentials: pull git code credentials
Click the [add] button to enter the jenkins add voucher pop-up window
**Branches to build: * * for branches, fill in * / master, master branch. If necessary, fill in other branches
cron timing configuration
Just click the question mark and see the help. It is set here to build a job at 6 a.m. every day
shell build
Select Execute shell here and execute with shell command. This is because Jenkins pulls the code in a fixed In the jenkins/workspace directory, the ${WORKSPACE} variable points to this address.
Finally save it!
Post build actions: after the action is built, click the add post build action option and select allow. The name filled in the path of Results should be the same as the name after ${WORKSPACE}, otherwise the report content is empty.
Run Jenkins
You can view the new task on the home page and click the small icon on the far right to build it. You can also click name to enter the task and click start construction. Click Allure Report to view the test results of this run.
Detailed Jenkins configuration: https://blog.csdn.net/weixin_42923777/article/details/102551493?ops_request_misc=&request_id=&biz_id=102&utm_term=jenkins%E9%85%8D%E7%BD%AEpytest +allure&utm_ medium=distribute. pc_ search_ result. none-task-blog-2allsobaiduweb~default-2-102551493. pc_ search_ result_ positive&spm=1018.2226.3001.4187