python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看

1.需求讲解

python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看_第1张图片
我们这个测试平台的主要功能有:上传储存用例,查看用例,运行用例,下载用例,上传测试报告等。

2.储存用例功能

2.1 创建用例表

(1)普通创建

第一步:创建app.py,用来启动一个flask服务

from flask import Flask
import conf
from flask_restful import Api,Resource

# 实例化一个Flask对象,用于启动flask服务
app = Flask(__name__)

# 添加配置文件
app.config.from_object(conf)

# 实例化flask_restful对象
api = Api(app)

# 设置用例储存视图函数
class TestCaseStoreView(Resource):
    def get(self):
        return '666'

api.add_resource(TestCaseStoreView, '/', endpoint='testcase_store')

if __name__ == "__main__":
    app.run(debug=True,host='127.0.0.1',port=5055)

第二步:创建conf.py文件,用于项目的配置文件

"""
flask项目的配置文件
"""

# 数据库连接配置
HOSTNAME = '192.168.1.104'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'ouyi1994'
DATABASE = 'auto_test_frame'
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
SQLALCHEMY_TRACK_MODIFICATIONS = True

第三步:创建models.py文件,用于创建用例表

"""
使用flask_sqlalchemy模块声明数据库表
"""
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy
from app import app

# 实例化一个SQLAlchemy对象,用于操作数据库
db = SQLAlchemy(app)

class TestCaseModel(db.Model):
    """
    用例表模型类
    """

    __tablename__ = 'testcase'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True) # id,主键
    name = db.Column(db.String(30), unique=True, nullable=False) # 用例名
    description = db.Column(db.String(200), unique=False, nullable=True) # 描述
    file_path = db.Column(db.String(200), unique=True, nullable=False) # 用例文件路径
    update_time = db.Column(db.DateTime, default=datetime.now()) # 更新时间

if __name__ == "__main__":
    db.create_all()

(2)优化db和数据库表初始化

db是SQLAlchemy的一个实例化对象,用于操作数据库。所以我们在项目中会经常将db对象导入到其他文件中操作。为了防止导入db时,因为循环导入报错。我们可以将db对象放置在一个单独的第三方文件中存放。

第一步:新建一个exit.py文件,用于生成db对象。

"""
中间文件
"""

from flask_sqlalchemy import SQLAlchemy

# 实例化一个SQLAlchemy对象,用于操作数据库
db = SQLAlchemy()

使用该文件可以有效防止引用冲突

第二步:创建app.py,用来启动一个flask服务

from flask import Flask
import conf
from flask_restful import Api,Resource
from exit import db

# 实例化一个Flask对象,用于启动flask服务
app = Flask(__name__)

# 添加配置文件
app.config.from_object(conf)

# 实例化flask_restful对象
api = Api(app)

# 将SQLAlchemy对象db关联到app
db.init_app(app)


# 设置用例储存视图函数
class TestCaseStoreView(Resource):
    def get(self):
        return '666'


api.add_resource(TestCaseStoreView, '/', endpoint='testcase_store')

if __name__ == "__main__":
    app.run(debug=True,host='127.0.0.1',port=5055)

第三步:创建models.py文件,用于创建用例表

"""
使用flask_sqlalchemy模块声明数据库表
"""
from datetime import datetime
from flask_sqlalchemy import SQLAlchemy
from exit import db

class TestCaseModel(db.Model):
    """
    用例表模型类
    """

    __tablename__ = 'testcase'
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(db.String(30), unique=True, nullable=False)
    description = db.Column(db.String(200), unique=False, nullable=True)
    file_path = db.Column(db.String(200), unique=True, nullable=False)
    update_time = db.Column(db.DateTime, default=datetime.now())

使用Flask_Migrate和flask_script来完成数据库的初始化和迁移

因为我们在项目开发中会频繁的改动数据库类结构,使用Flask_Migrate和flask_script可以非常方便的来完成数据库的初始化和迁移管理。所有的迁移操作其实都是Alembic做的,能跟踪模型的变化,并将变化映射到数据库中。
创建manage.py文件,用来完成数据库的管理。

"""
用于数据库初始化
"""

from flask_script import Manager
from flask_migrate import Migrate,MigrateCommand
from exit import db
from app import app
from models import TestCaseModel # 将需要初始化或者迁移的表模型类导入到该文件中

manage = Manager(app)

Migrate(app,db)

manage.add_command('db',MigrateCommand)

if __name__ == '__main__':
    manage.run()
  • 在命令行执行python manage.py db init生成数据表初始化的迁移文件migrates
    python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看_第2张图片
  • 在命令行执行python manage.py db migrate,自动检测模型,生成迁移脚本。
  • 在命令行python manage.py db upgrade,将迁移脚本的操作映射到数据库中。

2.2 实现储存用例的功能

(1)接口定义

在这里插入图片描述

(2)普通方式实现接口

编辑app.py文件

import os
from datetime import datetime

from flask import Flask, request
import conf
from flask_restful import Api, Resource
from exit import db
from models import TestCaseModel

# 实例化一个Flask对象,用于启动flask服务
app = Flask(__name__)

# 添加配置文件
app.config.from_object(conf)

# 实例化flask_restful对象
api = Api(app)

# 将SQLAlchemy对象db关联到app
db.init_app(app)


# 设置用例储存视图函数
class TestCaseStoreView(Resource):
    def post(self):
        name = request.form.get('name') # 获取请求参数:name
        description = request.form.get('description') # 获取请求参数:description
        test_file = request.files.get('test_file') # 获取请求参数:test_file
        test_file_name = test_file.filename # 用例文件名。

        # 判断name参数是否符合规定
        if name is not None and isinstance(name,str):

            # 判断test_file参数是否符合规定
            if test_file is not None and test_file_name.endswith('.py'):

                # 判断description参数是否为空
                if description is not None:

                    # 判断判断description参数是否为str类型
                    if isinstance(description,str):
                        now = datetime.now().strftime('%Y-%m-%d-%H%M%S')  # 生成时间戳
                        now_path = os.getcwd()  # 获取当前目录路径
                        test_file.save(f'./{
       now}_{
       test_file_name}')  # 保存用例文件到当前目录路径
                        file_path = os.path.join(now_path, f'{
       now}_{
       test_file_name}')  # 拼接出用例文件路径
                        testcase = TestCaseModel(name=name, file_path=file_path,description=description)
                        # 保存到数据库
                        db.session.add(testcase)
                        db.session.commit()
                        return jsonify({
     
                            'code': 200,
                            'message': 'success'
                        })
                    else:
                        return jsonify({
     
                            'code': 404,
                            'message': 'description必须为str类型'
                        })

                else:
                    now = datetime.now().strftime('%Y-%m-%d-%H%M%S') # 生成时间戳
                    now_path = os.getcwd() # 获取当前目录路径
                    test_file.save(f'./{
       now}_{
       test_file_name}') # 保存用例文件到当前目录路径
                    file_path = os.path.join(now_path,f'{
       now}_{
       test_file_name}') # 拼接出用例文件路径
                    testcase = TestCaseModel(name=name,file_path=file_path)
                    # 保存到数据库
                    db.session.add(testcase)
                    db.session.commit()
                    return jsonify({
     
                        'code':200,
                        'message':'success'
                    })

            else:
                return jsonify({
     
                    'code': 404,
                    'message': 'test_file参数不能为空,且必须为python文件'
                })
        else:
            return jsonify({
     
                'code':404,
                'message':'name参数不能为空,且必须为str类型'
            })





api.add_resource(TestCaseStoreView, '/testcase_store/', endpoint='testcase_store')

if __name__ == "__main__":
    app.run(debug=True,host='127.0.0.1',port=5055)

我们使用postman来构造数据访问接口,看接口功能是否实现。
python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看_第3张图片

结果如下:
python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看_第4张图片
python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看_第5张图片
python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看_第6张图片

(3)优化请求参数校验

  • 在上面的接口实现中,我们需要使用了if语句来校验前端传过来的参数。这种方式是不可取的,因为会使得我们的代码是否冗余。我们可以看到这个接口中只有3个参数,确使用了大量的if语句来进行校验。

  • 我们可以使用flask-wtf来校验前端参数。

新建一个form.py文件,来进行参数的校验工作。

from wtforms import Form, StringField, FileField
from wtforms.validators import Length, InputRequired, ValidationError
from flask_wtf.file import FileRequired


# 校验用例储存请求参数
class TestCaseForm(Form):
    name = StringField(validators=[Length(max=30, message='参数name不合法,参数长度不能大于30'),
                                   InputRequired(message='参数name不能为空')
                                   ])
    description = StringField(validators=[Length(max=200, message='参数description不合法,参数长度不能大于200')])
    test_file = FileField(validators=[FileRequired(message='必须上传用例文件')])

    def validate_test_file(self, field):
        """自定义test_file验证器"""
        if not field.data.filename.endswith('.py'): # field.data获取需要校验的参数值,这里为上传的文件对象
            raise ValidationError('用例文件必须为python文件') # 使用ValidationError抛出提示信息

修改app.py文件:

import os
from datetime import datetime
from flask import Flask, request, jsonify
import conf
from flask_restful import Api, Resource
from exit import db
from forms import TestCaseForm, GetTestCaseForm
from models import TestCaseModel
from werkzeug.datastructures import CombinedMultiDict


# 实例化一个Flask对象,用于启动flask服务
app = Flask(__name__)

# 添加配置文件
app.config.from_object(conf)

# 实例化flask_restful对象
api = Api(app)

# 将SQLAlchemy对象db关联到app
db.init_app(app)


# 设置用例储存视图函数
class TestCaseStoreView(Resource):
    def post(self):

        now = datetime.now().strftime('%Y-%m-%d-%H%M%S')  # 生成时间戳
        now_path = os.getcwd()  # 获取当前目录路径

        # 实例化TestCaseForm对象,用于参数校验。其参数为需要校验的参数字典列表
        # CombinedMultiDict将两个属性组合起来传给校验器
        form = TestCaseForm(CombinedMultiDict([request.form,request.files]))


        if form.validate():

            # 参数校验通过
            name = form.name.data # 获取参数name的值
            description = form.description.data # 获取参数description的值
            test_file = form.test_file.data # 获取用例文件test_file对象

            # 判断name是否已在数据库中存在
            name_fag = db.session.query(TestCaseModel).filter(TestCaseModel.name==name).first()
            if name_fag is  None:
                if description is  not None:
                    # 保存用例文件
                    file_path = os.path.join(now_path, f'./case_files/{
       now}_{
       test_file.filename}')
                    test_file.save(file_path)

                    # 保存数据库
                    testcase = TestCaseModel(name=name,description=description,file_path=file_path)
                    db.session.add(testcase)
                    db.session.commit()

                    res = {
     
                        'code': 200,
                        'message': 'success'
                    }
                    return jsonify(res)
                else:
                    # 保存用例文件
                    file_path = os.path.join(now_path, f'./case_files/{
       now}_{
       test_file.filename}')
                    test_file.save(file_path)

                    # 保存数据库
                    testcase = TestCaseModel(name=name, file_path=file_path)
                    db.session.add(testcase)
                    db.session.commit()

                    res = {
     
                        'code': 200,
                        'message': 'success'
                    }
                    return jsonify(res)
            else:
                res = {
     
                    'code': 404,
                    'message': '用例名字不能重复'
                }
                return jsonify(res)

        else:
            # 参数校验不通过
            res = {
     
                'code': 404,
                'message': form.errors
            }
            return jsonify(res)

api.add_resource(TestCaseStoreView, '/testcase_store/', endpoint='testcase_store')

if __name__ == "__main__":
    app.run(debug=True,host='127.0.0.1',port=5055)

运行结果如下:

python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看_第7张图片
python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看_第8张图片
python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看_第9张图片
python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看_第10张图片

3.查询用例功能

3.1 接口分析

在这里插入图片描述

3.2 实现接口

修改form.py文件

from wtforms import Form, StringField, FileField, IntegerField
from wtforms.validators import Length, InputRequired, ValidationError
from flask_wtf.file import FileRequired


# 校验用例储存请求参数
class TestCaseForm(Form):
    name = StringField(validators=[Length(max=30, message='用例名字不合法,参数长度不能大于30'),
                                   InputRequired(message='用例名字不能为空')
                                   ])
    description = StringField(validators=[Length(max=200, message='用例描述不合法,参数长度不能大于200')])
    test_file = FileField(validators=[FileRequired(message='必须上传用例文件')])

    def validate_test_file(self, field):
        """自定义test_file验证器"""
        if not field.data.filename.endswith('.py'): # field.data获取需要校验的参数值,这里为上传的文件对象
            raise ValidationError('用例文件必须为python文件') # 使用ValidationError抛出提示信息

class GetTestCaseForm(Form):
    id = IntegerField(validators=[InputRequired('用例id不能为空')])


修改app.py

import os
from datetime import datetime
from flask import Flask, request, jsonify
import conf
from flask_restful import Api, Resource
from exit import db
from forms import TestCaseForm, GetTestCaseForm
from models import TestCaseModel
from werkzeug.datastructures import CombinedMultiDict


# 实例化一个Flask对象,用于启动flask服务
app = Flask(__name__)

# 添加配置文件
app.config.from_object(conf)

# 实例化flask_restful对象
api = Api(app)

# 将SQLAlchemy对象db关联到app
db.init_app(app)


# 设置用例储存视图函数
class TestCaseStoreView(Resource):
    def post(self):

        now = datetime.now().strftime('%Y-%m-%d-%H%M%S')  # 生成时间戳
        now_path = os.getcwd()  # 获取当前目录路径

        # 实例化TestCaseForm对象,用于参数校验。其参数为需要校验的参数字典列表
        # CombinedMultiDict将两个属性组合起来传给校验器
        form = TestCaseForm(CombinedMultiDict([request.form,request.files]))


        if form.validate():

            # 参数校验通过
            name = form.name.data # 获取参数name的值
            description = form.description.data # 获取参数description的值
            test_file = form.test_file.data # 获取用例文件test_file对象

            # 判断name是否已在数据库中存在
            name_fag = db.session.query(TestCaseModel).filter(TestCaseModel.name==name).first()
            if name_fag is  None:
                if description is  not None:
                    # 保存用例文件
                    file_path = os.path.join(now_path, f'./case_files/{
       now}_{
       test_file.filename}')
                    test_file.save(file_path)

                    # 保存数据库
                    testcase = TestCaseModel(name=name,description=description,file_path=file_path)
                    db.session.add(testcase)
                    db.session.commit()

                    res = {
     
                        'code': 200,
                        'message': 'success'
                    }
                    return jsonify(res)
                else:
                    # 保存用例文件
                    file_path = os.path.join(now_path, f'./case_files/{
       now}_{
       test_file.filename}')
                    test_file.save(file_path)

                    # 保存数据库
                    testcase = TestCaseModel(name=name, file_path=file_path)
                    db.session.add(testcase)
                    db.session.commit()

                    res = {
     
                        'code': 200,
                        'message': 'success'
                    }
                    return jsonify(res)
            else:
                res = {
     
                    'code': 404,
                    'message': '用例名字不能重复'
                }
                return jsonify(res)

        else:
            # 参数校验不通过
            res = {
     
                'code': 404,
                'message': form.errors
            }
            return jsonify(res)

# 查看用例视图函数
class GetTestCaseView(Resource):
    def get(self):
        form = GetTestCaseForm(request.args)
        if form.validate():
            id = form.id.data
            id_fag = db.session.query(TestCaseModel).filter(TestCaseModel.id==id).first()

            # 查看id是否在数据库中存在
            if id_fag is not None:
                res = {
     
                    'code': 200,
                    'id': id_fag.id,
                    'name': id_fag.name,
                    'description': id_fag.description,
                    'file_path': id_fag.file_path,
                    'update_time': str(id_fag.update_time)
                }
                return jsonify(res)
            else:
                res = {
     
                    'code': 404,
                    'message': '未找到对应的用例信息'
                }
                return jsonify(res)

        else:
            res = {
     
                'code':404,
                'message':form.errors
            }
            return jsonify(res)

api.add_resource(TestCaseStoreView, '/testcase_store/', endpoint='testcase_store')
api.add_resource(GetTestCaseView, '/get_testcase/', endpoint='get_testcase')

if __name__ == "__main__":
    app.run(debug=True,host='127.0.0.1',port=5055)

为了防止返回的json数据在浏览器中中文乱码,我们需要给增加app增加一项配置
修改conf.py文件:

"""
flask项目的配置文件
"""

# 数据库连接配置
HOSTNAME = '192.168.1.104'
PORT = 3306
USERNAME = 'root'
PASSWORD = 'ouyi1994'
DATABASE = 'auto_test_frame'
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://{}:{}@{}:{}/{}?charset=utf8'.format(USERNAME, PASSWORD, HOSTNAME, PORT, DATABASE)
SQLALCHEMY_TRACK_MODIFICATIONS = True

# 防止浏览器乱码
JSON_AS_ASCII = False

运行结果如下:
python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看_第11张图片
python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看_第12张图片
python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看_第13张图片

4.拓展

4.1 url的末尾是否需要/

我们在视图函数中定义url地址时,最好加上/。因为这样我们在浏览器上访问url时,不论是否加上/都可以访问到。实际上是浏览器会自动补上/

4.2 学会调试

我们之所以使用pycharm这种开发工具来编写代码,有一个重要的原因是方便调试。

例如:我们创建了一个视图函数并映射了一个url,我们想要获取到url请求中的参数。但是我们并不知道这个参数是中request的哪个属性下,这时我们就可以使用debug模式

第一步:在pycharm中启动debug模式
python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看_第14张图片
第二步:在浏览器访问url
在这里插入图片描述
第三步:调试,找到我们想要的参数
python自动化(七)自动化测试平台开发:4.后端开发之用例的储存和查看_第15张图片
当然调试并不是只有这一个作用,大家在使用过程中可以自己多摸索摸索

总结:

到这里我们就完成了,用例储存及查询用例两个后台接口的开发。当然这种实现逻辑并不是很好,因为我们只是教学讲解,所以用较简单的方式来完成它即可。

你可能感兴趣的:(python自动化,python,flask,软件测试)