(一)想使用flaskrestful 开发一个测试用例管理平台

序言

之前公司用例管理这块比较混乱、格式与编写内容不统一。 于是想要对测试编写与归档规范化、平台化。

打算使用flaskrestful + mysql 先把后端搭建起来。前端具体使用react 还是 vue 到时候再说

本文中为随笔、记录开发过程中遇到的问题与笔记。更新无规律。甚至整体结构都没有想好 -,-

版本

Flask==2.0.2
Py == 3.10

Start

因为之前写过flask 所以先把基础架子搭建好。

数据库大体结构v1版本

entity

目录结构
  • APP
    • Controller
      • init 蓝本
      • view 视图
    • init
    • auth 权限等
  • Comment
    • myException 异常获取
    • myResponse response 封装
  • Config
    • projectConfig 环境config
  • Enums
    • 一些枚举值
  • Models
    • 实体类
  • Utils
    • 工具
  • resource
  • main.py - 启动入口

结构暂定如此、边写边改

配置Config

casesHub/Configs/projectConfig.py

class ProjectConfig: 
    SECRET_KEY = 'hard to guess string'

    SQLALCHEMY_COMMIT_ON_TEARDOWN = True
    SQLALCHEMY_TRACK_MODIFICATIONS = False
    HOST = "127.0.0.1"
    redisPort = '6379'

    # FLASKY_MAIL_SUBJECT_PREFIX = '[Flasky]'
    # FLASKY_MAIL_SENDER = 'Flasky Admin <flasky@example.com>'
    # FLASKY_ADMIN = os.environ.get('FLASKY_ADMIN')

    @staticmethod
    def init_app(app):
        pass

class DevelopmentConfig(ProjectConfig):
    DEBUG = True

    CACHE_TYPE = 'simple'
    ERROR_404_HELP = False
    CACHE_DEFAULT_TIMEOUT = 300
    # MAIL_SERVER = 'smtp.googlemail.com'
    # MAIL_PORT = 587
    # MAIL_USE_TLS = True
    # MAIL_USERNAME = os.environ.get('MAIL_USERNAME')
    # MAIL_PASSWORD = os.environ.get('MAIL_PASSWORD')
    JSON_AS_ASCII = False  # 这个配置可以确保http请求返回的json数据中正常显示中文

#    SQLALCHEMY_DATABASE_URI = 'sqlite:///' + os.path.join(basedir, 'data-dev.sqlite')  #使用sqlite
    SQLALCHEMY_DATABASE_URI = "mysql+pymysql://root:root@localhost:3306/caseHub" #使用mysql
    SQLALCHEMY_ECHO = True #打印sql语法
    SQLALCHEMY_TRACK_MODIFICATIONS = True #如果设置成 True (默认情况),Flask-SQLAlchemy 将会追踪对象的修改并且发送信号。这需要额外的内存, 如果不必要的可以禁用它

config = {
    "dev": DevelopmentConfig, #dev环境
    "test": TestingConfig,	#测试环境
    "pro": ProjectConfig, #线上环境
    "default": DevelopmentConfig
}

APP

casesHub/App/__init__.py /
工厂函数app


catch = Cache()
db = SQLAlchemy(query_class=MyBaseQuery)
auth = HTTPBasicAuth()

def create_app(configName: AnyStr = "default") -> Flask:
    """
    初始化app
    定义环境配置
    定义中文
    支持跨域
    注册蓝本
    :param configName: projectConfig
    :return: app
    """

    app = Flask(__name__)
    app.config.from_object(config[configName])
    app.config["BABEL_DEFAULT_LOCALE"] = "zh"
    catch.init_app(app)  # 支持缓存
    db.init_app(app)  # db绑定app

    CORS(app, supports_credentials=True)

    from .userController import userBP
    app.register_blueprint(userBP)  

    return app

蓝本

casesHub/App/userController/__init__.py

from flask import Blueprint

userBP = Blueprint("user", __name__, url_prefix="/v1/api/user")

from . import register

url_prefix 路由前缀
userBP 蓝本对象需要注册到create_app
register 视图函数、实例化蓝本对象后引入 否则会抛出引用错误

注册视图函数

casesHub/App/userController/register.py /

class RegisterController(Resource):

    def post(self) -> jsonify:
        """
        注册
        :return: jsonify
        """
        parse = MyRequestParseUtil()
        parse.add(name="username", type=str, required=True)
        parse.add(name="phone", type=str, required=True)
        parse.add(name="password", type=str, required=True)
        parse.add(name="email", type=str, required=True)
        parse.add(name="gender", type=str, choices=["MALE", "FEMALE"], required=False)
        parse.add(name="isAdmin", type=bool, required=False)
        parse.add(name="departmentID", type=int, required=True)
        body = parse.parse_args()
        User(**body).save()
        return MyResponse.success()

casesHub/Utils/requestParseUtil.py 自己写了个参数校验
主要校验参数类型、必传、是否符合预期 否则raise 抛出错误

class MyRequestParseUtil:

    def __init__(self, location: AnyStr = "json"):
        """
        :param location: location default json 
        """
        self.location = location
        self.args = []
        self.body = getattr(request, self.location, {})

    def add(self, **kwargs):
        """
        添加请求数据与数据类型
        :param kwargs: name type required default choices
        """
        # 默认类型为字符
        if not kwargs.get("type"):
            kwargs.setdefault("type", str)
        # 默认非必传
        if not kwargs.get("required"):
            kwargs.setdefault("required", False)
        self.args.append(kwargs)

    def parse_args(self) -> Dict:
        """
        参数校验
        :return: self.body
        """
        if self.body is None:
            raise ParamException(ResponseMsg.REQUEST_BODY_EMPTY)
        for kw in self.args:
            # 设定 必传 但未传
            if kw['required'] is True and not self.body.get(kw["name"]):
                raise ParamException(ResponseMsg.empty(kw["name"]))
            # 传空字符
            if self.body.get(kw['name']) == "":
                raise ParamException(ResponseMsg.empty(kw["name"]))
            # 传参未按照定义类型
            if not isinstance(self.body[kw['name']], kw['type']):
                raise ParamException(ResponseMsg.error_type(kw["name"], kw['type']))
            # 传参未按照指定区间
            if kw.get('choices'):
                if self.body[kw['name']] not in kw['choices']:
                    raise ParamException(ResponseMsg.error_val(kw["name"], kw['choices']))
            # 未传且设定默认
            if kw.get("default") and self.body.get(kw['name']) is None:
                self.body[kw['name']] = kw.get('default')

        return self.body

自定义返回与异常封装

自定义Exception

ParamException AuthException 等等 见源码

class MyException(HTTPException):
    """
    自定义 Exception 基类
    """

    def __init__(self, response: Dict = None):
        """
        :param response: MyResponse
        """
        if response:
            self._response = response
        else:
            self._response = MyResponse.error(ResponseCode.SERVER_ERROR)
        super(MyException, self).__init__(response=self.__make_response())

    def __make_response(self) -> Response:
        """
        自定义返回 response
        :return: Response
        """
        log.error(self._response)
        return Response(json.dumps(self._response), mimetype="application/json")

自定义Response

ParamError AuthError

class MyResponse:

    @staticmethod
    def success(data: Any = None) -> jsonify:
        return jsonify({"code": ResponseCode.SUCCESS, "data": data, "msg": ResponseMsg.OK})

    @staticmethod
    def error(code: ResponseCode) -> Dict:
        return {"code": code, "data": None, "msg": ResponseMsg.ERROR}


Models

总会有个Base 基类实体
对应继承基类


class Base(db.Model):
    __abstract__ = True
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    create_time = db.Column(db.Date, default=datetime.now, comment="创建时间")
    update_time = db.Column(db.Date, default=datetime.now, onupdate=datetime.now, comment="修改时间")

暂时User类就这么定义
还没想好关系处理。

class User(Base):
    __tablename__ = "user"
    username = db.Column(db.String(20), unique=True, comment="用户名")
    phone = db.Column(db.String(12), unique=True, comment="手机")
    password = db.Column(db.String(200), comment="密码")
    email = db.Column(db.String(40), unique=True, comment="邮箱")
    gender = db.Column(db.Enum("MALE", "FEMALE"), server_default="MALE", comment="性别")
    avatar = db.Column(db.LargeBinary, nullable=True, comment="头像")
    isAdmin = db.Column(db.Boolean, default=False, comment="管理")
    tag = db.Column(db.Enum("QA", "PR", "DEV"), comment="标签")

    from .departments import Department  # 不同文件下需引入
    departmentID = db.Column(db.INTEGER, db.ForeignKey("department.id"), nullable=False, comment="所属部门")

    from Models.ProjectModel.pro import Project,Product
    projectID = db.Column(db.INTEGER, db.ForeignKey("project.id"), nullable=True, comment="所属项目")
    productID = db.Column(db.INTEGER, db.ForeignKey("product.id"), nullable=True, comment="所属产品")

    def __init__(self, username: AnyStr, password: AnyStr, phone: AnyStr,
                 email: AnyStr, tag: AnyStr,
                 gender: AnyStr, isAdmin: bool, departmentID: int,
                 projectID: int = None, productID: int = None):
        self.username = username
        self.hash_password(password)
        self.email = email
        self.gender = gender
        self.phone = phone
        self.tag = tag
        self.isAdmin = isAdmin
        self.departmentID = departmentID

        self.productID = productID
        self.projectID = projectID

    def hash_password(self, password: AnyStr):
        """
        密码加密
        :param password:  password
        :return: hash_password
        """
        self.password = generate_password_hash(password)

    def generate_token(self, expires_time: int = 3600 * 24):
        """
        生成token
        :param expires_time: 过期时间 默认 一天
        :return: token
        """
        token = {"id": self.id, "expires_time": time.time() + expires_time}
        return jwt.encode(token, current_app.config["SECRET_KEY"], algorithm="HS256")

    @staticmethod
    def verify_token(token: AnyStr) -> Union[None, Any]:
        """
        token 解密
        :param token:
        :return: None or user
        """
        try:
            data = jwt.decode(token, current_app.config['SECRET_KEY'], algorithm=["HS256"])
        except Exception as e:
            log.error(e)
            return None
        return User.query.get(data['id'])

    def verify_password(self, password: AnyStr) -> bool:
        """
        校验密码
        :param password: password
        :return: bool
        """
        return check_password_hash(self.password, password)

    @property
    def admin(self) -> bool:
        """
        :return:
        """
        return self.isAdmin

    def __repr__(self):
        return f"<{User.__name__} {self.username}>"

BaseQuery

db = SQLAlchemy(query_class=MyBaseQuery) 初始化db时 选择

class MyBaseQuery(BaseQuery):

    # def filter_by(self, **kwargs):
    #     """
    #     ¹ýÂËÈíɾ³ý
    #     :param kwargs:
    #     :return:
    #     """
    #
    #     kwargs.setdefault('status', 1)
    #     return super().filter_by(**kwargs)

    def get_or_NoFound(self, ident, name):
        rv = self.get(ident)
        if not rv:
            raise ParamException(ResponseMsg.no_existent(name))
        return rv

后序

目前进度就是这样、有的模块没有录入 flask 也是边写边学、有好的想法的大佬可以留言。thank u
附上github github

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,以下是一个酒店管理系统的测试用例示例: 测试用例编号:HMS-001 测试用例名称:用户登录功能测试 测试目的:验证用户登录功能是否正常,能否成功登录系统并进入主界面。 测试环境:酒店管理系统正式版,Windows 10 操作系统,Chrome 浏览器。 测试步骤: 1. 打开 Chrome 浏览器,输入酒店管理系统网址。 2. 进入登录界面,输入正确的用户名和密码。 3. 点击“登录”按钮。 4. 验证是否成功登录系统,并进入主界面。 预期结果: 用户能够成功登录系统,并进入主界面。 实际结果: 用户成功登录系统,并进入主界面。 测试结论: 用户登录功能测试通过。 备注: 如果测试结果与预期结果不符,需要记录错误信息并及时通知开发人员进行修正。 测试用例编号:HMS-002 测试用例名称:添加客房信息测试 测试目的:验证添加客房信息功能是否正常,能否成功添加客房信息。 测试环境:酒店管理系统正式版,Windows 10 操作系统,Chrome 浏览器。 测试步骤: 1. 登录系统。 2. 进入客房管理界面,点击“添加客房”按钮。 3. 输入客房相关信息,包括房间号、房间类型、价格、状态等。 4. 点击“保存”按钮。 5. 验证客房信息是否成功添加。 预期结果: 客房信息能够成功添加到系统中。 实际结果: 客房信息成功添加到系统中。 测试结论: 添加客房信息测试通过。 备注: 如果测试结果与预期结果不符,需要记录错误信息并及时通知开发人员进行修正。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值