基于flask的web测试平台的开发流程


目录

一、简介 3

二、 项目架构 3

2.1、业务结构 3

2.2、平台架构 4

三、开发框架选择 4

3.1Web开发框架 4

3.2、数据库ORM 5

3.3、前端 5

四、项目结构 5

4.1 项目结构目录树 5

4.2 项目结构目录解析 6

五、数据库 6

5.1SQLAlchemy数据库配置 6

5.2、建立数据库表 8

5.3、数据库操作 8

六、 路由和视图 9

6.1、定义方法 9

6.2、变量规则 10

6.3HTTP 方法 11

6.4、模板渲染 11

七、 前端页面 11

7.1HTML基本结构 11

7.2、表单结构 12

7.3Jinja2模板渲染语法 13

7.3.1、变量 14

7.3.2、控制结构 14

7.3.3、模板继承 15

八、 前后台数据传递 16

8.1、注册/登录 17

8.2、权限控制 18

8.3、列表页数据获取 20

、用例加载执行 21

9.1、单元执行 21

9.2、批量执行 22

一、简介

该平台是一款基于CS模式的web服务平台,用于对测试用例的统一管理,集用例添加、编辑、删除、运行于一身,用例运行的核心机制采用的是httprunner

二、项目架构

2.1、业务结构

2.2、平台架构

三、开发框架选择

3.1Web开发框架

平台用例核心运行机制采用的是httprunner,而httprunner采用的Python语言,所以平台也顺势选用Python作为开发语言,Python web开发有两个主流框架DjangoFlask

l Django是一款重量级的Python开发web框架,功能齐全,不同的人用Django来开发,开发出来的东西基本架构都是一样的,

l Flask是一个轻量级的web框架,相对来说更加灵活,每个人开发出来的东西架构可能完全不一样。

对于团队级别的大型项目来说,比较适合选择Django,因为整个团队比较容易达成一致的架构,不会陷入无休止的会议讨论,里面很多东西不需要自己设计,拿来即用,快速开发。

对于个人开发的小项目来说,也可以用Django,也可以用Flask,用Flask的话,可以设计自己的项目架构,不必拘泥于Django的固定模式。对于以后进行功能扩展也比较容易开展。

l Flask对于加深Python学习有很大帮助,因为Flask不像Django很多东西都是现成的,需要自己去写

因此,基于以上的对比,选择Flask作为项目的开发框架。

3.2、数据库ORM

采用SQLAlchemySQLAlchemyPython编程语言下的一款ORM框架,该框架建立在数据库API之上,使用关系对象映射进行数据库操作,简言之便是:将对象转换成SQL,然后使用数据API执行SQL并获取执行结果。选择SQLAlchemy一个原因是因为它是基于Python的开源框架,再就是使用比较广泛,教程多,遇到问题容易找到解决方法。

3.3、前端

前端基本都是固定的HTML5,CSS,JavaScript,为了方便快速开发,选用一款前端框架bootstrap,脚本引入jQuery库。由于采用flask框架,因此模板渲染自然而然就是采用的jinja2.

四、项目结构

4.1 项目结构目录树

E:.

│  .gitignore

│  config.py

│  manager.py

│  run.py

├─app

│  │  models.py

│  │  views.py

│  │  __init__.py

│  ├─static

│  │  ├─bootstrap

│  │  │  ├─css

│  │  │  │      bootstrap-theme.css

│  │  │  ├─fonts

│  │  │  │      glyphicons-halflings-regular.eot

│  │  │  └─js

│  │  │          bootstrap.js

│  │  └─image

│  │          bg.jpg

│  ├─templates

│  │      add_config.html

│  └─__pycache__

│          models.cpython-36.pyc

│          __init__.cpython-36.pyc

├─hrunner

│  │

│  ├─ssj_api

│  ├─ssj_share

│  ├─ssj_sync

│  ├─token

│  └─user_api

├─httprunner

├─tests

└─test

4.2 项目结构目录解析

l .gitignore文件是放在项目的根目录,用来存放需要忽略的git提交文件

l config.py用来存放项目的一些配置信息

l manager.py用来存放一些通用的函数,数据库操作类

l app文件夹为项目的主应用

l models.py中用来存放数据库模型,所有的数据库表都在这里建立

l views.py中用来存放路由函数,所有的请求接口功能都是在这里定义的

l __init__.py中主要初始化一个应用的,里面除了import语句,只有一行代码app=Flask(__name__)

l static文件夹中用来存放一些网站的静态文件,如bootstrap框架的cssjs模板,一些image背景图片,图标之类的

l templates文件夹中用来存放网站每个html页面

l test中是测试代码

五、数据库

每个网站都有大量数据,需要数据库支撑,本项目采用MySQL数据库,在项目开始前首先要建立一个项目对应的数据库SsjApiPlat,并且需要提前安装Python第三方库Flask-SQLAlchemy,在使用SQLAlchemy时需要对数据库进行配置。在进行配置之前需要用到应用,就需要提前导入应用和必要的库文件

from flask_sqlalchemy import SQLAlchemy
from Flask.SsjApiPlat2.app import app

因为Python3以后都是默认采用绝对引用,所以引用的路径要补全。

5.1SQLAlchemy数据库配置

Flask-SQLAlchemy 扩展能够识别的配置键的清单:

SQLALCHEMY_DATABASE_URI

用于连接数据的数据库。例如:

· sqlite:////tmp/test.db

· mysql://username:password@server/db

SQLALCHEMY_BINDS

一个映射绑定 (bind) 键到 SQLAlchemy 连接 URIs 的字典。

SQLALCHEMY_ECHO

如果设置成 TrueSQLAlchemy 将会记录所有 发到标准输出(stderr)的语句,这对调试很有帮助。

SQLALCHEMY_RECORD_QUERIES

可以用于显式地禁用或者启用查询记录。查询记录 在调试或者测试模式下自动启用。

SQLALCHEMY_NATIVE_UNICODE

可以用于显式地禁用支持原生的 unicode。这是 某些数据库适配器必须的(像在 Ubuntu 某些版本上的 PostgreSQL),当使用不合适的指定无编码的数据库 默认值时。

SQLALCHEMY_POOL_SIZE

数据库连接池的大小。默认是数据库引擎的默认值 (通常是 5)。

SQLALCHEMY_POOL_TIMEOUT

指定数据库连接池的超时时间。默认是 10

SQLALCHEMY_POOL_RECYCLE

自动回收连接的秒数。这对 MySQL 是必须的,默认 情况下 MySQL 会自动移除闲置 8 小时或者以上的连接。 需要注意地是如果使用 MySQL 的话, Flask-SQLAlchemy 会自动地设置这个值为 2 小时。

SQLALCHEMY_MAX_OVERFLOW

控制在连接池达到最大值后可以创建的连接数。当这些额外的 连接回收到连接池后将会被断开和抛弃。

SQLALCHEMY_TRACK_MODIFICATIONS

如果设置成 True (默认情况)Flask-SQLAlchemy 将会追踪对象的修改并且发送信号。这需要额外的内存, 如果不必要的可以禁用它。

最主要的配置就是第一个SQLALCHEMY_DATABASE_URI,用于连接数据库,其实我们也只需要配置这一个键就可以了,其他的都可以选择默认。

SQLAlchemy 把一个引擎的源表示为一个连同设定引擎选项的可选字符串参数的 URIURI 的形式是:

dialect+driver://username:password@host:port/database

该字符串中的许多部分是可选的。如果没有指定驱动器,会选择默认的(确保在这种情况下 不 包含 + )。

项目中的MySQL数据库配置如下:

app.config['SQLALCHEMY_DATABASE_URI'] = "mysql://root:123456@localhost:3306/ssjapiplat"

还需要添加一个SECRET_KEY,这个字段是为以后进行账号权限控制使用的

app.config['SECRET_KEY'] = 'A0Zr98j/3yX R~XHH!jmN]LWX/,?RT'

如果你使用的是其他的数据库,可以参考下面的示例

Postgres:

postgresql://scott:tiger@localhost/mydatabase

MySQL:

mysql://scott:tiger@localhost/mydatabase

Oracle:

oracle://scott:[email protected]:1521/sidname

SQLite (注意开头的四个斜线):

sqlite:////absolute/path/to/foo.db

5.2、建立数据库表

数据库表需要提前规划好,比如一个需要注册登陆的网站,就需要一个用户数据表,数据表的设计都是放在models模块中,SQLAlchemy设计数据表的方法如下:

class UserInfo(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True)
    email = db.Column(db.String(120), unique=True)
    password = db.Column(db.String(80),)

    def __init__(self, username, email, password):
        self.username = username
        self.email = email
        self.password = password

    def __repr__(self):
        return '<User %s>' % self.username

最后调用db.create_all()来执行创建,在数据库中建立对应的数据表

5.3、数据库操作

userinfo = UserInfo(username=username,
                    email=email,
                    password=password
                    )
db.session.add(userinfo)
db.session.commit()

a= UserInfo.query.filter_by(username=username, password=password)

db.session.delete(a)

db.session.commit()

a= UserInfo.query.filter_by(username=username, password=password)

a.username = jeo

db.session.commit()

UserInfo.query.all()

UserInfo.query.filter_by(username=username, password=password).count()

UserInfo.query.with_entities(UserInfo.id).filter_by(username=username).all()  #用来查询数据库表中某一字段,返回的是一个列表,列表中的元素是一个元组

六、路由和视图

6.1、定义方法

Flask中路由就是实际服务器的接口,视图就是实现该接口的函数,在Flask定义路由的最简便方式,是使用程序实例提供的app.route修饰器,把修饰的函数注册为路由

如:

@app.route('/')

def index():

    return 'Index Page'

@app.route('/hello')

def hello():

    return 'Hello World'

index这样的函数为视图函数

如何访问路由?首先我们需要先启动路由,启动方法就是在main函数中执行app.run()即可,这样访问地址和端口都是使用的默认的,一般为127.0.0.1:8000,如果要从远程主机访问的话需要加入host参数如下设置

if __name__ == '__main__':

    app.run(host='0.0.0.0')

要访问index,在浏览器输入127.0.0.1:5000/,回车即可,返回结果如下图所示

Hello页面,127.0.0.1:5000/hello

这里有一个需要注意的问题

@app.route('/projects/')

def projects():

    return 'The project page'

 

@app.route('/projects)

def projects():

    return 'The project page'

上面两个例子看似一样,但他们的结尾斜线的使用在URL定义中不同

第一个例子,如果在访问时,结尾不加斜线,那么会被Flask重定向到带斜线的规范URL

第二个例子,如果在访问时,结尾加了斜线,那么访问时,会产生一个404not found的错误

所以,在编写路由时尽量在最后添加斜线

不仅如此!你可以构造含有动态部分的 URL,也可以在一个函数上附着多个规则。

6.2、变量规则

要给 URL 添加变量部分,你可以把这些特殊的字段标记为 <variable_name> , 这个部分将会作为命名参数传递到你的函数。规则可以用 <converter:variable_name> 指定一个可选的转换器。这里有一些不错的例子:

@app.route('/user/<username>')

def show_user_profile(username):

    # show the user profile for that user

    return 'User %s' % username

@app.route('/post/<int:post_id>')

def show_post(post_id):

    # show the post with the given id, the id is an integer

    return 'Post %d' % post_id

    ```

## 程序请求上下文

### Flask从客户端收到请求,需让视图函数能访问一些对象,才能处理请求。为了避免大量可有可无的参数把视图函数弄的一团糟,Flask使用上下文把某些对象变为全局可访问。### 如:

```Python

from Flask import request

@app.route('/')

def index():

    user=request.headers.get('User-Agent')

    return 'your browser is %s' % user

6.3HTTP 方法

HTTP (与 Web 应用会话的协议)有许多不同的访问 URL 方法。默认情况下,路由只回应 GET 请求,但是通过 route() 装饰器传递 methods 参数可以改变这个行为。这里有一些例子:

@app.route('/login', methods=['GET', 'POST'])

def login():

    if request.method == 'POST':

        do_the_login()

    else:

        show_the_login_form()

当我们打开一个登录页面时,http方法默认使用的是get,而当点击登录按钮时,会发起一个post的表单请求。

6.4、模板渲染

Python 生成 HTML 十分无趣,而且相当繁琐,因为你必须手动对 HTML 做转义来保证应用的安全。为此,Flask 配备了 Jinja2 模板引擎。

你可以直接使用 render_template() 方法来渲染模板。Flask中的render_template方法已经完全封装好了jinja的那一套模板渲染方法,你不需要再关心jinja中的environment对象,template对象和render方法,你需要做的一切就是将模板名和你想作为关键字的参数传入模板的变量。这里有一个展示如何渲染模板的简例:

from flask import render_template

@app.route('/hello/')

@app.route('/hello/<name>')

def hello(name=None):

    return render_template('hello.html', name=name)

我们所要关注的地方就是渲染方法传入的上下文参数和页面控制逻辑,这些将放在前端页面一节介绍

七、前端页面

前端页面部分的用到的主要有三大块:HTMLCSSJavaScript。页面样式和动作用到的CSSJavaScript这里采用了一个前端的框架bootstrap。页面的变量及控制用jinja2来渲染。

7.1HTML基本结构

下面是一个最简单的html5文档

<!DOCTYPE html>

<html>

<head>

<meta charset="utf-8">

<title>文档标题</title>

</head>

 

<body>

文档内容......

</body>

 

</html>

以上文档保存成.html格式后用浏览器打开就是如下图所示

这个文档中包含6个标签元素,都是必备的最基本的标签

l <!DOCTYPE>标签是用来告知web浏览器页面使用了哪个版本的HTMLHTML5还是HTML4或者其他版本

HTML5版本声明:

<!DOCTYPE html>

HTML4版本声明:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">

l <html>标签是告知浏览器这是一个html文档,是html文档最外层的元素

l <head>标签是所有头部元素的容器,里面必须要包含<title>元素,也可以包含脚本,样式等信息

l <title>标签用来表示浏览器工具栏文档的标题,页面添加收藏夹时的标题,搜索引擎搜索结果的标题,这个元素也是必须的

l <body>标签是文档的主体,里面包含了页面所展示的文字,图片,多媒体等一系列信息。

7.2、表单结构

下面代码就是一个注册页面的表单,这里只贴出了body部分中的用户名一个输入框。

Form表单中所有属于用户输入的元素最终都会在提交表单时,作为post请求的data一起发送。Div是用来进行页面布局的块。他们都有class属性用来描述这是一个什么类型的控件,style属性对应的是css样式,这两个属性的值都可以使用bootstrap中预设的各种值。

最终点击最后一个div中的注册按钮会触发onclick属性的值(一个ajax请求函数,这个函数定义在<javascript>标签中)

<body background="/static/image/bg.jpg" style="padding-top: 50px;">

    <form class="form-horizontal"role="form" id="register_form">

        <div class="form-group">

            <h2 class="text-center">欢迎使用HTTPRUNNER</h2>

        </div>

        <div class="form-group">

            <label for="txtUserName" class="col-sm-5 control-label">用户名</label>

            <div class="col-sm-5">

                <input type="text" style="width: 200px" class="form-control" id="txtUserName" name="username" placeholder="请输入用户名" required>

                <span id="username.info" style="color:red"></span>

            </div>

        </div>

<div class="form-group">

            <div class="col-sm-10 col-sm-offset-5">

                <button type="submit" class="btn btn-primary" onclick="register_ajax('#register_form')">注册</button>

            </div>

        </div>

</form>

</body>

下面这个表单是登录页面的,只贴出了form一行,这个表单的请求方式与上面的注册页面是不一样的,表单中的action属性值指示了表单请求的urlmethod属性值指示了请求的方法

<form class="form-horizontal" action="/api/login/" role="form" method="POST" id="login_form">

两种请求方式的区别在于:

表单请求的content-type值为:applicaiton/x-www-form-urlencoded

Ajax发起的请求的content-type值为:application/json

7.3Jinja2模板渲染语法

模板包含 变量  表达式 ,这两者在模板求值的时候会被替换为值。模板中 还有标签,控制模板的逻辑。

下面是一个最小的模板,它阐明了一些基础。我们会在文档中后面的部分解释细节:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"><html lang="en"><head>

    <title>My Webpage</title></head><body>

    <ul id="navigation">

    {% for item in navigation %}

        <li><a href="{{ item.href }}">{{ item.caption }}</a></li>

    {% endfor %}

    </ul>

 

    <h1>My Webpage</h1>

    {{ a_variable }}</body></html>

 

这里有两种分隔符: {% ... %}  {{ ... }} 。前者用于执行诸如 for 循环 或赋值的语句,后者把表达式的结果打印到模板上。

7.3.1、变量

如果一个变量包含属性,可以通过如下方式来访问它的属性

{{ foo.bar }}

{{ foo['bar'] }}

7.3.2、控制结构

控制结构指的是所有的那些可以控制程序流的东西 —— 条件(比如 if/elif/ekse )、 for 循环、以及宏和块之类的东西。控制结构在默认语法中以 {% .. %} 块的形式 出现。

如下代码是在一个页面中嵌入一个表格,表格中的元素就是通过for循环来提取并插入的,并通过loop.index循环迭代为每一行插入序号

<table class="table table-bordered table-hover">

<thead>

<tr>

<th>#</th>

<th>序号</th>

<th>项目名称</th>

<th>负责人</th>

<th>创建日期</th>

<th>操作</th>

</tr>

</thead>

<tbody>

{% for foo in project %}

  <tr>

<td><input type="checkbox" name="project_{{ foo.id }}" value="{{ foo.id }}"></td>

<td>{{ loop.index }}</td>

<td>{{ foo.project_name }}</td>

<td>{{ foo.response_name }}</td>

<td>{{ foo.response_name }}</td>

  </tr>

{% endfor %}

</tbody>

</table>

还有其他的一些特殊的变量:

变量

描述

loop.index

当前循环迭代的次数(从 1 开始)

loop.index0

当前循环迭代的次数(从 0 开始)

loop.revindex

到循环结束需要迭代的次数(从 1 开始)

loop.revindex0

到循环结束需要迭代的次数(从 0 开始)

loop.first

如果是第一次迭代,为 True

loop.last

如果是最后一次迭代,为 True

loop.length

序列中的项目数。

loop.cycle

在一串序列间期取值的辅助函数。

7.3.3、模板继承

Jinja 中最强大的部分就是模板继承。模板继承允许你构建一个包含你站点共同元素的基 本模板“骨架”,并定义子模板可以覆盖的  

基本模板

这个模板,我们会把它叫做 base.html ,定义了一个简单的 HTML 骨架文档,你可能使用一个简单的两栏页面。用内容填充空的块是子模板的工作,下面是简化了的项目中的base.html,其中有两个block,一个是title一个是content,所有的js库和前端框架都在基本模板加载一次:

<!doctype html>

<html class="js cssanimations" lang="zh-CN">

<head>

    <meta charset="utf-8">

    <title>{% block title %}{% endblock %}</title>

 

    <meta name="apple-mobile-web-app-title" content="HttpRunnerManager"/>

    <link href="/static/bootstrap/css/bootstrap.min.css"  rel="stylesheet" type="text/css">

    <link href="/static/bootstrap/css/bootstrap-theme.min.css"  rel="stylesheet" type="text/css">

    <link href="https://cdn.bootcss.com/bootstrap-validator/0.5.3/css/bootstrapValidator.min.css" rel="stylesheet">

 

    <script src="/static/bootstrap/js/jquery.min.js"></script>

    <script src="/static/bootstrap/js/bootstrap.min.js"></script>

    <script src="https://cdn.bootcss.com/jquery.serializeJSON/2.9.0/jquery.serializejson.min.js"></script>

    <script src="https://cdn.bootcss.com/bootstrap-validator/0.5.3/js/bootstrapValidator.min.js"></script>

 

</head>

<body>

<div >

一些公共内容

 

{% block content %}{% endblock %}

 

<script type="text/javascript">

</script>

</div>

</body>

</html>

子模板

那么我们如何使用基本模板来继承呢?

看下面的一段简化的代码,首先{% extend %}标签就是用来指定继承哪个模板的,它应该放在第一行,然后再继承标题块展示子模板自己的标题,最后继承内容块填充子模板自己的页面内容。

{% extends "base.html" %}

{% block title %}项目列表{% endblock %}

{% block content %}

<div class="col-md-offset-2" style="bottom: 0;right: 0;top: 35px;left: 210px;">

    子模板的内容

</div>

{% endblock %}

更多内容请参考官方中文文档:

http://docs.jinkan.org/docs/jinja2/templates.html#template-inheritance

八、前后台数据传递

从前面几节可以看到一个web网站基本有数据库,路由函数,前端页面三大部分组成,这其中涉及到很多数据传递的问题,比如一个用户进行注册,填写的页面数据是如何传递给后台,并储存到数据库中的,用户登录时页面数据时如何与数据库存储的数据进行对比验证的。这一节将针对前后台的数据传递进行讲解。

8.1、注册/登录

 

1、打开注册页面,浏览器输入注册地址http://127.0.0.1:5000/api/register/,会首先在views.py视图中查找路由,发起一个方法默认为gethttp请求,然后调用路由对应的视图函数,判断为get方法后会调用render_template(‘register.html’)返回一个经过渲染后的注册页面

2、填写注册信息,这些信息就对应了页面的form表单信息,当我们点击注册按钮时,会调用页面的JavaScript代码利用form控件中的id来识别表单,并获取表单中的input控件信息将信息进行json序列化,序列化后的格式为{input name属性值:input控件输入值},通过jQuery.ajax()将方法,urlconten-typedata都设置完成后发起一个http请求。

3、请求中的url我们仍然设置的是注册页面的路由,但是这次的请求方法是post,视图函数判断为post方法后,会从请求体中提取请求的datarequest.json[‘input name属性值’],然后再调用数据库的表类创建一个以获取的数据为参数的对象,并插入数据库(参见5.3节)

这样一个账号注册的数据传递就完成了

后面的添加项目、添加模板、添加配置等都是一样的原理

8.2、权限控制

当我们注册了一个账号后,可以用该账号进行登录,但是有一个问题,如果我们通过在浏览器直接输入访问项目列表的页面时,会成功的跳转到项目列表页,而且页面的左上角也没有账号信息。

这里就需要通过session进行权限控制,记得我们前面设置的SECRET_KEY吧,就是用在这里的

在登录的视图函数中,如果用户信息验证成功,则为该用户设置一个session,并重定向到首页,首页的欢迎您处接受渲染模板传递的参数“username”。

        if UserInfo.query.filter_by(username=username, password=password).count() == 1:

            request.session["login_status"] = True

            request.session["now_account"] = username

            return redirect(url_for('index', username=username))

然后再定义一个注销的函数,用来删除now_account

这样,页面就显示用户信息了,还可以进行注销,但是之前的直接访问指定页面问题依然存在,并且出现了一个新问题,就是同时登录两个账户时,注销了一个后,另一个也被注销了,他们的session共享了。

直接访问指定页面问题

解决方法:将当前登录用户的用户名传递到所有的路由url中,想要访问指定页面,首先要知道用户名,然后还要通过session的判断,如果session中没有该用户或者用户的登录状态为false就返回到登录页面

多用户登录session共享问题

解决方法:修改session的保存形式,session以列表的形式保存每个登录的用户信息,每个用户以字典的形式保存登录状态和用户名。

修改session的数据结构

        if UserInfo.query.filter_by(username=username, password=password).count() == 1:

            user = {'login_status': True, 'now_account': username}

            session[username] = user

            return redirect(url_for('index', username=username))

        else:

            return render_template("login.html")

 

在所有的url中添加用户名

@app.route('/api/index/<username>', methods=['GET', 'POST'])

def index(username):

    if username in session.keys() and session[username]['login_status'] == True:

        return render_template('index.html',

                               username=username,

                               now_account=username,

                               user=username,

                               prolist=username,

                               addpro=username,

                               modlist=username,

                               addmod=username,

                               caselist=username,

                               conflist=username,

                               addconf=username,

                               addcase=username,

                               )

    else:

        return redirect("/api/login/")

同样,在所有有页面跳转的链接中添加用户名变量,所以在render_template渲染模板时,我们需要传递所有链接所用到的用户名

<li><a href="/api/{{ prolist }}/project_list/1/">项 目 列 表</a></li>

<li><a href="/api/{{ addpro }}/add_project/">新 增 项 目</a></li>

8.3、列表页数据获取

在项目列表、模块列表、配置列表、用例列表页面中,都嵌入一个表格,用来查看所有的项目等信息,列表页数据获取流程如下:

1、首先发起项目列表的请求,然后通过数据库查询获取所有的项目信息保存在project对象中

2、然后返回模板渲染,并传入project参数

3、项目列表页中的project变量就会接收传过来的project对象,代码如下,通过jinja的控制循环提取所有信息渲染到表格中

<table class="table table-bordered table-hover">

<thead>

<tr>

<th>#</th>

<th>序号</th>

<th>项目名称</th>

<th>负责人</th>

<th>创建日期</th>

<th>操作</th>

</tr>

</thead>

<tbody>

{% for foo in project %}

  <tr>

<td><input type="checkbox" name="project_{{ foo.id }}" value="{{ foo.id }}"></td>

<td>{{ loop.index }}</td>

<td>{{ foo.project_name }}</td>

<td>{{ foo.response_name }}</td>

<td>{{ foo.response_name }}</td>

  </tr>

{% endfor %}

</tbody>

</table>

、用例加载执行

用例添加分两种,页面添加和文件导入,页面添加暂时不支持添加变量和函数,文件导入可以支持变量和函数(不过目前也正在做的过程中);用例执行在底层还是用的httprunner,所以平台的主要功能就是从数据库中查询数据,转换成用例列表即可,然后调用httprunner执行,用例执行分四个等级:单元执行(单元执行又分单用例执行,模块执行,项目执行),批量执行。

9.1、用例添加

9.1.1、页面添加

9.1.2、文件导入

通过文件导入的用例,用例源文件会保存到服务器上,用例中的内容通过解析后提取用例内容,然后存储到数据库中,再从数据库中查询出刚才的用例并传递到前端,渲染到页面列表显示出来

9.3、单元执行

单元执行流程图:

单元执行步骤:

1、点击列表最右侧的“运行”按钮,发起一个表单请求,并在请求中设置从表单中获取的按钮对应行的单元的idmoderun_by_singlerun_by_modulerun_by_project

2、根据请求的url匹配视图中的路由函数,在函数中判断mode类型来执行下一步

3、如果是run_by_single,则直接根据id从数据库中查询对应的用例,并调用run_by_single()函数生成用例列表,然后跳到6

4、如果是run_by_module,则首先根据id从数据库中查询对应的模块,在根据模块名从数据库中查询对应的用例,获取用例id,跳转到3

5、如果是run_by_project,则首先根据id从数据库中查询对应的项目,再根据项目名从数据库中查询对应的用例,获取用例id,跳转到3

6、调用httprunner运行前面已经生成的用例列表,最后的结果再进行模板渲染返回结果页面。

猜你喜欢

转载自blog.csdn.net/zoulonglong/article/details/80083696
今日推荐