<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      基于pytest和allure構(gòu)建自動化測試框架與項目

      代碼:https://gitee.com/kunmzhao/auto_test_-project.git

      框架目錄結(jié)構(gòu)

      我們要構(gòu)建一個自動化測試框架,就要以項目的概念來對項目中的所有代碼文件進行劃分目錄和文件結(jié)構(gòu),需要設(shè)計一個合理的目錄結(jié)構(gòu),以便與測試開發(fā)團隊的其他人員的開發(fā)和測試,也便于項目的維護

      設(shè)計的項目目錄如下

      根目錄
      ├── api                  # 封裝測試項目的api接口[用于mock測試]
      │   └── __init__.py
      ├── config.py            # 項目代碼配置文件
      ├── data                 # 測試數(shù)據(jù)/測試用例存放目錄
      ├── libs                 # 第三方lib庫
      ├── main.py              # 項目入口
      ├── pytest.ini           # pytest模塊的配置文件
      ├── reports              # HTML測試報告生成目錄
      ├── results              # 測試報告生成目錄
      ├── tests                # 測試用例腳本存放目錄
      ├── utils                # 自定義工具類
      ├── requirments.txt      # 項目依賴模塊 
       

       

      配置文件 config.py

      import pathlib  # 路徑操作模塊,替代os.path模塊,可以通過對象的方式操作路徑
      
      # 項目目錄的主目錄文件路徑[字符串]
      BASE_DIR_STR = pathlib.Path(__file__).parent.resolve().as_posix()
      
      # 項目目錄的主目錄路徑[路徑對象]
      BASE_DIR = pathlib.Path(BASE_DIR_STR)
      
      # 項目名字
      PROJECT_NAME = "接口自動化測試框架"
      
      # 自動化測試項目的運行IP和端口
      HOST = "127.0.0.1"
      PORT = 8088
      
      if __name__ == '__main__':
          print(BASE_DIR_STR, type(BASE_DIR_STR))
          print(BASE_DIR, type(BASE_DIR))

       

      入口文件main.py

      import pytest
      import os
      import sys
      import shutil
      
      import config
      
      if __name__ == '__main__':
          try:
              # 刪除之前存在的報告文件夾
              shutil.rmtree("./reports")
              shutil.rmtree("./results")
          except Exception as e:
              # TODO:需要添加日志
              print(e)
      
          # 啟動 pytest 測試框架,啟動參數(shù)配置在 pytest.ini 中
          pytest.main()
      
          # 生成測試報告
          os.system(f"{config.BASE_DIR.joinpath('libs/allure/bin/allure')} generate  ./results -o ./reports")
      
          # 打開測試報告
          os.system(f"{config.BASE_DIR.joinpath('libs/allure/bin/allure')} serve ./results  -h {config.HOST} -p {config.PORT}")

      pytest.ini配置文件

      [pytest]
      # 指定運行參數(shù)
      addopts = -s -v -p no:warnings  --alluredir=./results
      
      # 搜索測試文件的目錄路徑
      testpaths = ./
      
      # 搜索測試文件名格式
      python_files = test_*.py
      
      # 搜索測試類格式
      python_classes = Test*
      
      # 搜索測試方法名格式
      python_functions = test_*

      下面編寫一個測試用例,來確保框架可以正常運行

      test_login.py

      import allure
      import config
      
      
      @allure.epic(config.PROJECT_NAME)
      @allure.feature("用戶模塊")
      @allure.story("用戶登錄")
      class TestUser(object):
          def test_username_by_empty(self):
              allure.dynamic.title("用戶名為空,登錄失敗")
              allure.dynamic.description("測試用戶名為空的描述")
              allure.attach("文件內(nèi)容", "log")
              assert 1 == 2

      運行main.py

       新增日志功能

      日志模塊是項目中必不可缺的模塊,便于我們查看項目運行狀況和排查錯誤

      config.py新增日志配置

      import pathlib  # 路徑操作模塊,替代os.path模塊,可以通過對象的方式操作路徑
      
      # 項目目錄的主目錄文件路徑[字符串]
      BASE_DIR_STR = pathlib.Path(__file__).parent.resolve().as_posix()
      
      # 項目目錄的主目錄路徑[路徑對象]
      BASE_DIR = pathlib.Path(BASE_DIR_STR)
      
      # 項目名字
      PROJECT_NAME = "接口自動化測試框架"
      
      # 自動化測試報告的運行IP和端口
      HOST = "127.0.0.1"
      PORT = 8088
      
      # 日志模塊配置
      LOGGING = {
          "name": "Zeekr",  # 日志處理器名稱
          "filename": (BASE_DIR / "logs/zeeker.log").as_posix(),  # 日志文件儲存路徑
          "charset": "utf-8",
          "backup_count": 31,  # 日志文件的備份數(shù)量
          "when": "d"  # 日志文件創(chuàng)建間隔時間為每天創(chuàng)建一個
      }
      
      if __name__ == '__main__':
          print(BASE_DIR_STR, type(BASE_DIR_STR))
          print(BASE_DIR, type(BASE_DIR))

      logger.py模塊

      import logging
      import config
      
      from logging import handlers
      
      
      class LogHandler(object):
          """日志處理工具類"""
      
          def __init__(self, name=None, filename=None):
              """
              :param name: 日志處理器的名字
              :param filename: 日志文件名字
              """
              # 如果沒有指定name和filename,則使用配置文件配置
              self.name = name or config.LOGGING.get("name", "pytest")
              self.filename = filename or config.LOGGING.get("filename", "pytest.log")
      
              self.charset = config.LOGGING.get("charset", "utf-8")
              self.backup_count = config.LOGGING.get("backup_count", 31)
              self.when = config.LOGGING.get("when", "d")
      
              self.logger = None
      
          def get_logger(self):
              """
              創(chuàng)建 logger
              :return: logger 對象
              """
              # 避免重復(fù)創(chuàng)建logger TODO:使用到單例模式是否更好?
              if self.logger:
                  return self.logger
      
              logger = logging.getLogger(self.name)
              # 設(shè)置日志初始化等級
              logger.setLevel(logging.DEBUG)
      
              # 創(chuàng)建handler
              fh = handlers.TimedRotatingFileHandler(
                  filename=self.filename,
                  when=self.when,
                  backupCount=self.backup_count,
                  encoding=self.charset
              )
      
              sh = logging.StreamHandler()
      
              # 為每種handler設(shè)置日志等級
              # fh.setLevel(logging.INFO)
      
              # 設(shè)置輸出日志格式
              simple_formater = logging.Formatter(fmt="【{levelname}】 {name} {module}: {lineno} {message}", style="{")
              verbose_formater = logging.Formatter(
                  fmt="【{levelname}】 {asctime} {name} {pathname}: {lineno} {message}",
                  datefmt="%Y-%m-%d %H:%M:%S",
                  style="{"
              )
      
              # 為handler指定輸出格式
              fh.setFormatter(verbose_formater)
              sh.setFormatter(simple_formater)
      
              # 為logger添加日志處理器
              logger.addHandler(fh)
              logger.addHandler(sh)
      
              self.logger = logger
      
              return logger
      
      
      if __name__ == '__main__':
          logger = LogHandler().get_logger()
      
          logger.debug("測試 debug")
          logger.info("測試 info")
          logger.warning("測試 warning")
          logger.error("測試 error")

      在測試用例中簡單使用日志

      import allure
      import config
      from utils.logger import LogHandler
      
      logger = LogHandler().get_logger()
      
      
      @allure.epic(config.PROJECT_NAME)
      @allure.feature("用戶模塊")
      @allure.story("用戶登錄")
      class TestUser(object):
          def test_username_by_empty(self):
              allure.dynamic.title("用戶名為空,登錄失敗")
              allure.dynamic.description("測試用戶名為空的描述")
              allure.attach("文件內(nèi)容", "log")
              logger.debug("測試 debug")
              logger.info("測試 info")
              logger.warning("測試 warning")
              logger.error("測試 error")

      運行main.py文件

       

      【DEBUG】 2023-09-26 10:16:33 Zeekr /home/zk4956z3/PycharmProjects/Stark/utils/logger.py: 75 測試 debug
      【INFO】 2023-09-26 10:16:33 Zeekr /home/zk4956z3/PycharmProjects/Stark/utils/logger.py: 76 測試 info
      【W(wǎng)ARNING】 2023-09-26 10:16:33 Zeekr /home/zk4956z3/PycharmProjects/Stark/utils/logger.py: 77 測試 warning
      【ERROR】 2023-09-26 10:16:33 Zeekr /home/zk4956z3/PycharmProjects/Stark/utils/logger.py: 78 測試 error
      【DEBUG】 2023-09-26 10:18:34 Zeekr /home/zk4956z3/PycharmProjects/Stark/tests/users/test_login.py: 15 測試 debug
      【INFO】 2023-09-26 10:18:34 Zeekr /home/zk4956z3/PycharmProjects/Stark/tests/users/test_login.py: 16 測試 info
      【W(wǎng)ARNING】 2023-09-26 10:18:34 Zeekr /home/zk4956z3/PycharmProjects/Stark/tests/users/test_login.py: 17 測試 warning
      【ERROR】 2023-09-26 10:18:34 Zeekr /home/zk4956z3/PycharmProjects/Stark/tests/users/test_login.py: 18 測試 error
      【DEBUG】 2023-09-27 09:02:13 Zeekr /home/zk4956z3/PycharmProjects/Stark/tests/users/test_login.py: 16 測試 debug
      【INFO】 2023-09-27 09:02:13 Zeekr /home/zk4956z3/PycharmProjects/Stark/tests/users/test_login.py: 17 測試 info
      【W(wǎng)ARNING】 2023-09-27 09:02:13 Zeekr /home/zk4956z3/PycharmProjects/Stark/tests/users/test_login.py: 18 測試 warning
      【ERROR】 2023-09-27 09:02:13 Zeekr /home/zk4956z3/PycharmProjects/Stark/tests/users/test_login.py: 19 測試 error

       

      封裝請求工具

      接口的測試一般離不開http請求,在python中常用的http請求模塊有urllib, requests,httpx等,不過小編最常用的還是requests

      安裝

      pip install requests

      簡單使用

      import requests
      
      ####### GET請求 ########
      # 發(fā)送簡單的get請求 response
      = requests.get("https://baidu.com")
      # 發(fā)送有參數(shù)的get請求
      params = {
      "name": "kunmzhao",
      "age":18
      } response = requests.get("https://httpbin.org", params=params)
      # 獲取原生內(nèi)容 print(response.content) # 獲取文本內(nèi)容 print(response.text)

      # 接收json格式內(nèi)容
      print(response.json())

      # 接收二進制內(nèi)容
      with open("1.png", "wb") as fd:
        fd.write(response.content)

      ####### POST請求 ########
      # 發(fā)送表單數(shù)據(jù)
      import requests

      forms = {"name":"kunmzhao","age":18}
      response = requests.post("http://httpbin.org/post", data=forms)
      print(response.text)

      # 發(fā)送json數(shù)據(jù)
      json_data = {"name":"kunmzhao","age":18}
      response = requests.post("http://httpbin.org/post", json=json_data)
      print(response.text)
      # 文件上傳 支持一張或者多張數(shù)據(jù)的上傳
      files = {"avata":open("1.png",'rb')}
      response = requests.post("http://httpbin.org/post", files=files)
      print(response.text)

      ##### 發(fā)送請求頭 #####

      headers = {
        "User-Agent":"Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/117.0"
      }
      response = requests.get("https://www/zhihu.com/explore", headers=headers)
      print(response.text)

       

      requestor.py 對常用的http請求操作進行封裝

      import requests
      from utils.logger import LogHandler
      
      
      class Request(object):
          """http請求工具類"""
      
          def __init__(self):
              # 實例化session管理器,維持會話
              self.session = requests.session()
              self.logger = LogHandler().get_logger()
      
          def send(self, method, url, params=None, data=None, json=None, headers=None, **kwargs):
              """
              發(fā)送http請求
      
              :param method: 請求方法
              :param url: 請求URL
              :param params: 請求參數(shù)
              :param data: 請求數(shù)據(jù)
              :param json: jason傳參,請求數(shù)據(jù)
              :param headers: : 請求頭
              :param kwargs: 其它參數(shù)
              :return: headers
              """
              try:
                  self.logger.info(f"請求方法: {method}")
                  self.logger.info(f"請求url: {url}")
                  self.logger.info(f"請求params: {params}")
                  self.logger.info(f"請求data: {data}")
                  self.logger.info(f"請求json: {json}")
                  self.logger.info(f"請求headers: {headers}")
                  self.logger.info(f"請求額外參數(shù): {kwargs}")
      
                  response = self.session.request(method=method,
                                                  url=url,
                                                  params=params,
                                                  data=data,
                                                  json=json,
                                                  headers=headers,
                                                  **kwargs)
                  self.logger.info(f"回復(fù)狀態(tài)碼: {response.status_code}")
                  self.logger.info(f"回復(fù)響應(yīng)頭: {response.headers}")
                  self.logger.info(f"回復(fù)響應(yīng)體[二進制]: {response.content}")
                  self.logger.info(f"回復(fù)響應(yīng)體[純文本]: {response.text}")
                  self.logger.info(f"回復(fù)響應(yīng)體[json]: {response.json()}")
                  return response
              except Exception as e:
                  self.logger.error(f"請求錯誤,錯誤信息:{e}")
      
          def __call__(self, method, url, params=None, data=None, json=None, headers=None, **kwargs):
              """
              將對象當(dāng)做一個函數(shù)來使用,Request(method, url, params=None, data=None, json=None, headers=None, **kwargs)
              """
              return self.send(method=method,
                               url=url,
                               params=params,
                               data=data,
                               json=json,
                               headers=headers,
                               **kwargs)
      
      
      if __name__ == '__main__':
          request = Request()
          res = request("GET", "http://httpbin.org/get")
          print(res)
      
          res = request("post", "http://httpbin.org/post", json={"username": "kunmzhao"})
          print(res)

       

      基于Flask四線Mockserver

      在實際的項目開發(fā)中,經(jīng)常出現(xiàn)服務(wù)端和客戶端分離的情況,我們在做測試開發(fā)的時候,有可能服務(wù)的功能還沒有實現(xiàn),我們可以自己先模擬出服務(wù)端返回結(jié)果,以實現(xiàn)聯(lián)調(diào),等服務(wù)器上線后,切換server即可

      Flask是一個輕量級的python web框架,非常適合在測試中構(gòu)建模擬api服務(wù)器

      安裝模塊

      pip install flask
      pip install pymysql
      pip install flask_sqlalchemy

       

      api/__init__.py

      import config
      from flask import Flask
      from flask_sqlalchemy import SQLAlchemy
      
      db = SQLAlchemy()
      
      app = Flask(__name__)
      
      
      def init_app():
          # 加載配置
          app.config.from_object(config)
          # 加載數(shù)據(jù)庫配置
          db.init_app(app)
          # db創(chuàng)建數(shù)據(jù)庫
          with app.app_context():
              db.create_all()
      
          return app

      config.py

      import pathlib  # 路徑操作模塊,替代os.path模塊,可以通過對象的方式操作路徑
      
      # 項目目錄的主目錄文件路徑[字符串]
      BASE_DIR_STR = pathlib.Path(__file__).parent.resolve().as_posix()
      
      # 項目目錄的主目錄路徑[路徑對象]
      BASE_DIR = pathlib.Path(BASE_DIR_STR)
      
      # 項目名字
      PROJECT_NAME = "接口自動化測試框架"
      
      # 自動化測試報告的運行IP和端口
      HOST = "127.0.0.1"
      PORT = 8089
      
      # 日志模塊配置
      LOGGING = {
          "name": "Zeekr",  # 日志處理器名稱
          "filename": (BASE_DIR / "logs/zeeker.log").as_posix(),  # 日志文件儲存路徑
          "charset": "utf-8",
          "backup_count": 31,  # 日志文件的備份數(shù)量
          "when": "d"  # 日志文件創(chuàng)建間隔時間為每天創(chuàng)建一個
      }
      
      
      """mock server 的服務(wù)端配置"""
      # 數(shù)據(jù)庫連接
      SQLALCHEMY_DATABASE_URI: str = "mysql+pymysql://root:123@127.0.0.1:3306/pytest?charset=utf8mb4"
      # 查詢時會顯示原始SQL語句
      SQLALCHEMY_ECHO: bool = True
      # 調(diào)試模式
      DEBUG = True
      # 監(jiān)聽端口
      API_PORT = 8000
      # 監(jiān)聽地址
      API_HOST = "0.0.0.0"
      
      if __name__ == '__main__':
          print(BASE_DIR_STR, type(BASE_DIR_STR))
          print(BASE_DIR, type(BASE_DIR))

      api/models.py

      from datetime import datetime
      from werkzeug.security import generate_password_hash, check_password_hash
      
      from . import db
      
      
      class BaseModel(db.Model):
          """公共模型"""
          __abstract__ = True  # 抽象模型
          id = db.Column(db.Integer, primary_key=True, comment="主鍵ID")
          name = db.Column(db.String(255), default="", comment="名稱/標題")
          is_deleted = db.Column(db.Boolean, default=False, comment="邏輯刪除")
          orders = db.Column(db.Integer, default=0, comment="排序")
          status = db.Column(db.Boolean, default=True, comment="狀態(tài)(是否顯示,是否激活)")
          created_time = db.Column(db.DateTime, default=datetime.now, comment="創(chuàng)建時間")
          updated_time = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now, comment="更新時間")
      
          def __repr__(self):
              return f"<{self.__class__.__name__}: {self.name}>"
      
      
      class User(BaseModel):
          """用戶基本信息表"""
          __tablename__ = "py_user"
          name = db.Column(db.String(255), index=True, comment="用戶賬戶")
          nickname = db.Column(db.String(255), comment="用戶昵稱")
          _password = db.Column(db.String(255), comment="登錄密碼")
          intro = db.Column(db.String(500), default="", comment="個性簽名")
          avatar = db.Column(db.String(255), default="", comment="頭像url地址")
          sex = db.Column(db.SmallInteger, default=0, comment="性別")  # 0表示未設(shè)置,保密, 1表示男,2表示女
          email = db.Column(db.String(32), index=True, default="", nullable=False, comment="郵箱地址")
          mobile = db.Column(db.String(32), index=True, nullable=False, comment="手機號碼")
      
          # 存取器
          @property
          def password(self):  # user.password
              return self._password
      
          @password.setter
          def password(self, rawpwd):  # user.password = '123456'
              """密碼加密"""
              self._password = generate_password_hash(rawpwd)
      
          def check_password(self, rawpwd):
              """驗證密碼"""
              return check_password_hash(self.password, rawpwd)

      api/views.py

      
      
      from flask import request
      from sqlalchemy import or_

      from . import app
      from .models import User,db


      @app.route("/user/register", methods=["POST"])
      def register():
      """
      用戶信息注冊
      :return:
      """
      try:
      data = request.json
      # 創(chuàng)建用戶數(shù)據(jù)
      user = User(**data)
      db.session.add(user)
      db.session.commit()
      return {"msg": "注冊成功!", "data": {"id":user.id, "name": user.name}}, 200
      except Exception as e:
      return {"msg": "注冊失敗!", "data": {}}, 400


      @app.route("/user/login", methods=["POST"])
      def login():
      """
      用戶登錄
      :return:
      """
      user = User.query.filter(
      or_(
      User.mobile == request.json.get("mobile"),
      User.name == request.json.get("name"),
      User.email == request.json.get("email")
      )
      ).first() # 實例化模型

      if not user:
      return {"msg": "登錄失敗!用戶不存在!", "data": {}}, 400
      if not user.check_password(request.json.get("password")):
      return {"msg": "登錄失敗!密碼錯誤!", "data": {}}, 400

      return {"msg": "登錄成功", "data":{"id": user.id, "name": user.name}}, 200
      
      

      api/run.py

      import config
      from api import init_app
      
      # 注意,務(wù)必把模型models的內(nèi)容以及 views 中的服務(wù)端接口引入當(dāng)前文件,否則flask不識別。
      from api import models
      from api import views
      
      app = init_app()
      
      if __name__ == '__main__':
          app.run(host=config.API_HOST, port=config.API_PORT)

       

      test_login.py

      import allure
      import config
      from utils.logger import LogHandler
      from utils.requestor import Request
      
      logger = LogHandler().get_logger()
      SERVER_URl = f"http://{config.API_HOST}:{config.API_PORT}"
      
      @allure.epic(config.PROJECT_NAME)
      @allure.feature("用戶模塊")
      @allure.story("用戶登錄")
      class TestUser(object):
          def test_username_by_empty(self):
              allure.dynamic.title("用戶名為空,登錄失敗")
              allure.dynamic.description("測試用戶名為空的描述")
              allure.attach("文件內(nèi)容", "log")
              request = Request()
              response = request("POST", f"{SERVER_URl}/user/login", json={
                  "name": "",
                  "password": "123"
              })
              print(response)
      
          def test_password_by_empty(self):
              allure.dynamic.title("密碼為空,登錄失敗")
              allure.dynamic.description("測試密碼為空的描述")
              allure.attach("文件內(nèi)容", "log")
              request = Request()
              response = request("POST", f"{SERVER_URl}/user/login", json={
                  "name": "kunmzhao",
                  "password": ""
              })
              print(response)

      此時運行main.py文件和run.py文件,即可測該框架是否成功

       

      基于數(shù)據(jù)驅(qū)動生成用例代碼

      在實際測試開發(fā)中,我們一般使用參數(shù)化來自動生成測試用例,參數(shù)化用例一般采用json,yaml或者excle文件儲存,如果用例非常多,也可以改用數(shù)據(jù)庫

      下面介紹通過excel來承載測試用例,python中操作excel文件的模塊有xlrd+xlwt,pyexcle+openpyxl

      安裝模塊

      pip install xlrd
      pip install xlwt

       

      封裝excel工具類

      excel.py

      """
      不識別xlsx的文件后綴名,文件格式必須設(shè)置為xls
      """
      import xlrd
      import json
      
      
      class Excel(object):
          """Excel文件寫工具類, TODO:關(guān)于excel寫操作后續(xù)補充"""
      
          def __init__(self, filename):
              self.workbook = xlrd.open_workbook(filename, formatting_info=True)
      
          def get_sheet_names(self):
              """
              獲取當(dāng)前excle中所有sheet的名字
              """
              return self.workbook.sheet_names()
      
          def __get_sheet(self, sheet_index_or_name):
              """
              根據(jù)sheet的索引或者名字獲取對應(yīng)sheet對象
              """
              if isinstance(sheet_index_or_name, int):
                  if len(self.get_sheet_names()) > sheet_index_or_name:
                      return self.workbook.sheet_by_index(sheet_index_or_name)
                  raise Exception(
                      "無效的的sheet下標數(shù)值, excel文檔最大值為{}, 請求為數(shù)值為{}".format(
                          len(self.get_sheet_names()),
                          sheet_index_or_name),
                  )
              elif isinstance(sheet_index_or_name, str):
                  if sheet_index_or_name in self.get_sheet_names():
                      return self.workbook.sheet_by_name(sheet_index_or_name)
                  raise Exception("無效的sheet名字,名字為{}的sheet不存在".format(sheet_index_or_name))
      
              raise Exception("sheet_index_or_name只能為int或者str,但是入?yún)閧}".format(type(sheet_index_or_name)))
      
          def get_sheet_rows_num(self, sheet_index_or_name):
              """
              獲取指定sheet的的數(shù)據(jù)總行數(shù)
              """
              return self.__get_sheet(sheet_index_or_name).nrows
      
          def get_sheet_cols_num(self, sheet_index_or_name):
              """
              獲取指定sheet的數(shù)據(jù)總列數(shù)
              """
              return self.__get_sheet(sheet_index_or_name).ncols
      
          def get_cell_value(self, sheet_index_or_name, row_index, col_index):
              """
              獲取指定sheet中的指定單元格數(shù)據(jù)
              """
              sheet = self.__get_sheet(sheet_index_or_name)
              try:
                  return sheet.cell_value(row_index, col_index)
              except Exception as e:
                  raise Exception(str(e))
      
          def get_sheet_data(self, sheet_index_or_name, fields, first_line_is_header=True):
              """
              獲取工作表的所有數(shù)據(jù)
              """
              rows = self.get_sheet_rows_num(sheet_index_or_name)
              cols = self.get_sheet_cols_num(sheet_index_or_name)
      
              data = []
              for row in range(int(first_line_is_header), rows):
                  row_data = {}
                  for col in range(cols):
                      cell_data = self.get_cell_value(sheet_index_or_name, row, col)
                      if type(cell_data) is str and ("{" in cell_data and "}" in cell_data) or (
                              "[" in cell_data and "]" in cell_data):
                          cell_data = json.loads(cell_data)
                      print(col, fields[col])
                      row_data[fields[col]] = cell_data
                  data.append(row_data)
              return data
      
      
      if __name__ == '__main__':
          xls = Excel("../data/外來人員.xls")
          fields = ["姓名", "身份證號", "手機號",
                    "人員在膠狀態(tài)", "目前詳細住址", "隨訪異常",
                    "隨訪異常備注", "是否已做核酸", "核酸結(jié)果",
                    "核酸采樣日期", "核酸采樣地點", "人員類別",
                    "重點地區(qū)", "到達重點地區(qū)時間", "離開重點地區(qū)時間",
                    "返回膠州日期", "請輸入行程詳情概述"]
      
          print(xls.get_sheet_names())
          print(xls.get_sheet_cols_num("Sheet0"))
          print(xls.get_sheet_rows_num(0))
          print(xls.get_cell_value(0, 1, 1))
          print(xls.get_sheet_data(0, fields))

       

      config.py

      import pathlib  # 路徑操作模塊,替代os.path模塊,可以通過對象的方式操作路徑
      
      # 項目目錄的主目錄文件路徑[字符串]
      BASE_DIR_STR = pathlib.Path(__file__).parent.resolve().as_posix()
      
      # 項目目錄的主目錄路徑[路徑對象]
      BASE_DIR = pathlib.Path(BASE_DIR_STR)
      
      # 項目名字
      PROJECT_NAME = "接口自動化測試框架"
      
      # 自動化測試報告的運行IP和端口
      HOST = "127.0.0.1"
      PORT = 8089
      
      # 日志模塊配置
      LOGGING = {
          "name": "Zeekr",  # 日志處理器名稱
          "filename": (BASE_DIR / "logs/zeeker.log").as_posix(),  # 日志文件儲存路徑
          "charset": "utf-8",
          "backup_count": 31,  # 日志文件的備份數(shù)量
          "when": "d"  # 日志文件創(chuàng)建間隔時間為每天創(chuàng)建一個
      }
      
      # excel測試用例字段格式
      FIELD_LIST = [
          "case_id",  # 用例編號
          "module_name",  # 模塊名稱
          "case_name",  # 用例名稱
          "method",  # 請求方式
          "url",  # 接口地址
          "headers",  # 請求頭
          "params_desc",  # 參數(shù)說明
          "params",  # 請求參數(shù)
          "assert_result",  # 預(yù)期結(jié)果
          "real_result",  # 實際結(jié)果
          "remark",  # 備注
      ]
      
      """mock server 的服務(wù)端配置"""
      # 數(shù)據(jù)庫連接
      SQLALCHEMY_DATABASE_URI: str = "mysql+pymysql://root:ZKMzkm36337@127.0.0.1:3306/pytest?charset=utf8mb4"
      # 查詢時會顯示原始SQL語句
      SQLALCHEMY_ECHO: bool = True
      
      # 調(diào)試模式
      DEBUG = True
      # 監(jiān)聽端口
      API_PORT = 8000
      # 監(jiān)聽地址
      API_HOST = "0.0.0.0"
      
      if __name__ == '__main__':
          print(BASE_DIR_STR, type(BASE_DIR_STR))
          print(BASE_DIR, type(BASE_DIR))

      基于excel文件實現(xiàn)數(shù)據(jù)驅(qū)動生成測試用例

      data/case_user.xls

       

      utils/assertor.py

      from utils.logger import LogHandler
      
      logger = LogHandler().get_logger()
      
      
      def assertor(assert_list, response):
          """斷言函數(shù)"""
          if type(assert_list) is not list:
              assert_list = [assert_list]
      
          for expr in assert_list:
              logger.info(f"開始斷言:assert {expr}")
              if expr:
                  # exec 內(nèi)置解釋器,可以把符合python語法的字符串當(dāng)成代碼來運行
                  exec(f"assert {expr}", {
                      "code": response.status_code,
                      "json": response.json(),
                      "text": response.text,
                      "content": response.content,
                      "headers": response.headers,
                  })
      
              logger.info(f"斷言通過:assert {expr}")
      
      
      if __name__ == '__main__':
          # Response就是模擬requests HTTP請求工具的返回結(jié)果對象
          class Response(object):
              status_code = 400
              text = "對不起,登陸失敗!"
              content = "對不起,登陸失敗!"
              headers = []
      
              @classmethod
              def json(cls):
                  return {"id": 1},
      
      
          assert_list = [
              "code == 400",
              "'失敗'in text",
          ]
      
          assertor(assert_list, Response())

       

      test_login.py

      import allure
      import pytest
      
      import config
      from utils.logger import LogHandler
      from utils.requestor import Request
      from utils.excle import Excel
      from utils.assertor import assertor
      
      logger = LogHandler().get_logger()
      SERVER_URl = f"http://{config.API_HOST}:{config.API_PORT}"
      
      
      @allure.epic(config.PROJECT_NAME)
      @allure.feature("用戶模塊")
      @allure.story("用戶登錄")
      class TestUser(object):
          @pytest.mark.parametrize("kwargs",
                                   Excel(config.BASE_DIR / "data/case_user.xls").get_sheet_data(0, config.FIELD_LIST))
          def test_login(self, kwargs):
              request = Request()
              allure.dynamic.title(kwargs.get("case_name"))
              request.logger.info(f"開始請求測試接口:{kwargs.get('case_name')}")
              if kwargs.get("method").lower() in ["get", "delete"]:
                  """發(fā)送get或delete"""
                  response = request(kwargs.get("method"), f'{kwargs.get("url")}', params=kwargs.get("params"))
              else:
                  """發(fā)送post,put,patch"""
                  response = request(kwargs.get("method"), f'{kwargs.get("url")}', json=kwargs.get("params"))
              assertor(kwargs.get("assert_result"), response)

       

      posted on 2023-10-03 09:25  阿明明  閱讀(142)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 日本精品成人一区二区三区视频| 亚洲深夜精品在线观看| 夜色福利站WWW国产在线视频| 欧美激情一区二区久久久| 成人永久免费A∨一级在线播放 | 中文字幕人妻中出制服诱惑| 精品无人乱码一区二区三区| 久久毛片少妇高潮| 日韩不卡在线观看视频不卡| 少妇夜夜春夜夜爽试看视频| 日本一道一区二区视频| 久久精品国产99麻豆蜜月| 日本人妻巨大乳挤奶水免费| 国产成人精品无码专区| 精品精品国产国产自在线| 国产精品伦人视频免费看| 黑人巨大亚洲一区二区久| 国产一二三四区中| 天天躁日日躁狠狠躁中文字幕| 亚洲综合久久精品哦夜夜嗨| 18禁在线一区二区三区| 亚洲欧美一区二区成人片| 一区二区三区放荡人妻| 女同亚洲精品一区二区三| 正在播放国产真实哭都没用| 成年女人黄小视频| 日本边添边摸边做边爱喷水| 后入内射无码人妻一区| 亚洲国产成人久久一区久久| 国产精品老熟女露脸视频| 人妻中文字幕亚洲精品| 精品国产成人午夜福利| 国产精品人妻中文字幕| 久久人与动人物a级毛片| 国产精品白丝久久AV网站| 亚洲69视频| 国产精品白浆免费视频| 动漫av网站免费观看| 99久久精品费精品国产一区二| 日本污视频在线观看| 亚洲国产区男人本色vr|