测试用例的简述

一 、什么是测试用例?

Flask 作为三个全栈架构,若是您只会 python,而不懂 javascript
的前端知识,如同是不可能支撑起你的 web 梦想的,比如,三个简短的页面
一部分刷新 作用,你就需求用到 ajax 的文化,当然,你仍可以使用 HTML5
的新特点 —— websocket成效,幸而 flask 还提供了四个 flask-socketio
插件,本文大家就追究一下以此 flask-scoketio插件的用法。

    
测试用例是为某些特殊目的而编辑的一组测试输入、执行尺度以及预期结果,以便测试某些程序路径或核实是还是不是满意有个别特定需要。

理解 websocket 协议

  • 葡京网上娱乐场,HTTP 协议只可以通过客户端发起呼吁来与客户端进行报纸揭橥 ——
    这是三个毛病。
  • 透过websocket
    协议,服务器可以积极向客户端推送新闻,客户端也得以主动向服务器发送消息,是的确的双向平等对话,属于服务器推送技术的一种。

    
通俗的讲:就是把我们测试系统的操作步骤用依照一定的格式用文字描述出来。

websocket 协议特性

  1. 创建在 TCP 共商之上,服务器端的贯彻比较便于。
  2. 与 HTTP 协议抱有美妙的包容性。暗中认同端口也是80和443,并且握手阶段选用HTTP 协议,因而握手时不易于屏蔽,能经过种种 HTTP 代理服务器。
  3. 数码格式相比轻量,质量费用小,通讯高效。
  4. 可以发送文书,也足以发送二进制数据。
  5. 从未有过同源限制,客户端能够与人身自由服务器通讯。
  6. 商事标识符是ws(倘使加密,则为wss),服务器网址就是 UPRADOL。

贰 、写测试用例有啥利益?

使用 flask-socketio

  • 理清思路,避免遗漏

设置插件

pip install flask-socketio

        那里是大家觉得最首要的少数,假若我们测试的连串大而复杂,大家可以把项目功效区划,根据每3个作用通过编制用例的主意来整理大家测试系统的笔触,防止遗漏掉要测试的成效点。

项目结构

本文是在《基于 flask 的 CRUD
操作》
的根底上加码了
webscoket 的效用,使用的是 init_app() 的花样加载 flask-socketio
插件,和网上的多数学科稍有分歧

flask-wtf-crud/
|-- env/
    |-- <python虚拟环境>
|-- app/ <项目的模块名称>
    |-- crud/ <前端蓝图>
        |-- __init__.py
        |-- views.py <路由和视图函数文件>
        |-- forms.py <表单类文件, wtforms插件必须项>
        |-- templates <HTML模板>
            |-- static <静态文件夹>
                |-- js <JavaScript 文件夹>
                    |-- crud.js # 异步请求的程序主要在此添加
    |-- XXXXXX/ <其它蓝图>
    |-- __init__.py
    |-- models.py <数据库模型文件>
|-- migrations/ <数据库表关系文件夹,Flask-Migrate迁移数据库时使用>
|-- config.py <项目的配置文件>
|-- manage.py <用于启动程序以及其它程序任务>
  • 盯住测试进行

将 flask-socketio 引入项目

       
通过编制测试用例,执行测试用例,大家得以很明白的通晓我们的测试进程。

修改 manage.py 内容

# -*- coding:utf-8 -*-
__author__ = '东方鹗'
__blog__ = u'http://www.os373.cn'

import os
from app import create_app, db, socketio
from app.models import User
from flask_script import Manager, Shell
from flask_migrate import Migrate, MigrateCommand


app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app=app)
migrate = Migrate(app=app, db=db)

def make_shell_context():
    return dict(app=app, db=db, User=User)


manager.add_command("shell", Shell(make_context=make_shell_context))
manager.add_command('db', MigrateCommand)
manager.add_command('run', socketio.run(app=app, host='0.0.0.0', port=5001)) # 新加入的内容


if __name__ == '__main__':
    manager.run()
  • 正史参考

修改 app/__init__.py 内容

# -*- coding:utf-8 -*-
__author__ = '东方鹗'
__blog__ = u'http://www.os373.cn'

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from config import config
from flask_socketio import SocketIO # 新加入的内容
db = SQLAlchemy()

async_mode = None
socketio = SocketIO()


def create_app(config_name):
    """ 使用工厂函数初始化程序实例"""
    app = Flask(__name__)
    app.config.from_object(config[config_name])
    config[config_name].init_app(app=app)

    db.init_app(app=app)

    socketio.init_app(app=app, async_mode=async_mode) # 新加入的内容

    # 注册蓝本crud
    from .crud import crud as crud_blueprint
    app.register_blueprint(crud_blueprint, url_prefix='/crud')

    return app

        在大家所做的项目中,可能会有不可胜数作用是如出一辙或类似的,大家对那类成效设计了测试用例,便于以往我们相见类似功能的时候可以做参考根据。

眼前蓝图的 views.py

# -*- coding:utf-8 -*-
__author__ = '东方鹗'
__blog__ = u'http://www.os373.cn'

from flask import render_template, redirect, request, current_app, url_for, flash, json
from . import crud
from ..models import User
from .forms import AddUserForm, DeleteUserForm, EditUserForm
from ..import db
from threading import Lock
from app import socketio # 新加入的内容
from flask_socketio import emit # 新加入的内容

# 新加入的内容-开始
thread = None
thread_lock = Lock()

def background_thread(users_to_json):
    """Example of how to send server generated events to clients."""
    while True:
        socketio.sleep(5) \\ 每五秒发送一次

        socketio.emit('user_response', {'data': users_to_json}, namespace='/websocket/user_refresh')
# 新加入的内容-结束

@crud.route('/', methods=['GET', 'POST'])
def index():

    return render_template('index.html')


@crud.route('/websocket', methods=['GET', 'POST'])
def websocket():
    add_user_form = AddUserForm(prefix='add_user')
    delete_user_form = DeleteUserForm(prefix='delete_user')
    if add_user_form.validate_on_submit():
        if add_user_form.role.data == u'True':
            role = True
        else:
            role = False
        if add_user_form.status.data == u'True':
            status = True
        else:
            status = False
        u = User(username=add_user_form.username.data.strip(), email=add_user_form.email.data.strip(),
                 role=role, status=status)
        db.session.add(u)
        flash({'success': u'添加用户<%s>成功!' % add_user_form.username.data.strip()})
    if delete_user_form.validate_on_submit():
        u = User.query.get_or_404(int(delete_user_form.user_id.data.strip()))
        db.session.delete(u)
        flash({'success': u'删除用户<%s>成功!' % u.username})

    users = User.query.all()

    return render_template('websocket.html', users=users, addUserForm=add_user_form, deleteUserForm=delete_user_form)


@crud.route('/websocket-edit/<user_id>', methods=['GET', 'POST'])
def user_edit(user_id):
    user = User.query.get_or_404(user_id)
    edit_user_form = EditUserForm(prefix='edit_user', obj=user)
    if edit_user_form.validate_on_submit():
        user.username = edit_user_form.username.data.strip()
        user.email = edit_user_form.email.data.strip()
        if edit_user_form.role.data == u'True':
            user.role = True
        else:
            user.role = False
        if edit_user_form.status.data == u'True':
            user.status = True
        else:
            user.status = False
        flash({'success': u'用户资料已修改成功!'})
        return redirect(url_for('.basic'))

    return render_template('edit_websocket.html', editUserForm=edit_user_form, user=user)

# 新加入的内容-开始
@socketio.on('connect', namespace='/websocket/user_refresh')
def connect():
    """ 服务端自动发送通信请求 """
    global thread
    with thread_lock:
        users = User.query.all()
        users_to_json = [user.to_json() for user in users]

        if thread is None:
            thread = socketio.start_background_task(background_thread, (users_to_json, ))
    emit('server_response', {'data': '试图连接客户端!'})


@socketio.on('connect_event', namespace='/websocket/user_refresh')
def refresh_message(message):
    """ 服务端接受客户端发送的通信请求 """

    emit('server_response', {'data': message['data']})
# 新加入的内容-结束

———- 上述内容是后端的内容,以下内容是将是前段的情节 ———-

  • 重复性

crud.js 内容

$(document).ready(function () {
    namespace='/websocket/user_refresh';
    var socket = io.connect(location.protocol + '//' + document.domain + ':' + location.port + namespace);
    $("#url_show").text("websocket URL: " + location.protocol + '//' + document.domain + ':' + location.port + namespace);

    socket.on('connect', function() { // 发送到服务器的通信内容
        socket.emit('connect_event', {data: '我已连接上服务端!'});
    });

    socket.on('server_response', function(msg) {
        \\ 显示接受到的通信内容,包括服务器端直接发送的内容和反馈给客户端的内容
        $('#log').append('<br>' + $('<div/>').text('接收 : ' + msg.data).html());
    });
    socket.on('user_response', function(msg) {
        //console.log(eval(msg.data[0]));
        //$('#users_show').append('<br>' + $('<div/>').text('接收 : ' + msg.data).html());
        var tbody = "";
        var obj = eval(msg.data[0]);
        $.each(obj, function (n, value) {
            var role = "";
            if (value.role===true){
                role = "管理员";
            }else {
                role = "一般用户";
            }
            var status = "";
            if (value.status===true){
                status = "正常";
            }else {
                status = "注销";
            }
            edit_url = "<a href=" +  location.protocol + '//' + document.domain + ':' + location.port + "/crud/websocket-edit/" + value.id + "> 修改</a>";
            delete_url = "<a href=\"javascript:delete_user_" + value.id + "()\">删除</a>";
            var trs = "";
            trs += "<tr><th>" + (n+1) + "</th><td>" + value.username + "</td><td>" + value.email + "</td><td>" + role + "</td><td>" + status + "</td><td>" + edit_url + " | " + delete_url +"</td></tr>";
            tbody += trs;
        })
        $('#users_show').empty();
        $('#users_show').append(tbody);
    });
});

       
大家测试多个系列不是一位测五次尽管测完的,必要几人再三的进展测试,那么我们就要求测试用例来规范和引导大家的测试行为。

来得结果

image

历次打开网页,会浮现服务端发送的始末——“试图连接客户端!”,其后,客户端再次来到给服务端——“作者已连接上服务端!”,而后又被服务端重返给客户端显示。

以下的报表内容显示数据局里的始末,每 5 秒局地刷新一次表格内容。

服务器后端 log 日志内容如下:

image

  • 告知领导,那事作者干过,不然外人怎么知道你测没测,测的健全不圆满,拿测试用例给他俩看呗!笔者就是照着这一个工作,呵呵!

总结

  1. 鉴于 flask 架构具有上下文的范围,在数据Curry 伸张删改
    内容的时候,表格的始末没有成形——即便部分已经举办了刷新。要想体现变化后的数据库内容,必须得重复开动一下
    flask 服务。
  2. 就完整的配备以来,在 flask 项目里添加 websocket
    协议,显得项目较重,完成3个部分刷新的效率依然用 ajax 比较不难。
  3. 欢迎铁汉可以给本身的连串指出修改意见,先行感激!!!

源码下载

叁 、测试用例的点子

参考

    
好呢,咱知道啥是测试用例了,也是知道怎么要写测试用例了,那毕竟应该怎么写?无从下手啊。我们在写测试用例从前,先读书两种格局,它是大家写测试用例的率领思想。

    1.  等价类划分

         在有个别输入域的子集合,在该子集合中,种种输入数据对于揭穿程序中的错误都以等价的。若是有二个输入框要求输入1-一千0个数,大家不容许用每一个数去试,大家输入5
和输入6去验证和揭穿输入框的错误可以当做是等价的。那么那么些时候我们就足以肆意的抽取一些数码来拓展认证。如:十 、9九 、7777……

       等价类分:有效等价类和低效等价类

       输入框要求输入1-一千0的数

       有效等价类:可以输入1-一千0之内的数来表明,如:贰 、五 、9九 、8495……

      
无效等价类:能够输入1-一千0之外的任意字符验证,如:30000、字母、下划线、特殊符号、空格、回车…..

    2.  边界值

       边界值是对等价类的增补,测试工作经历告诉大家,多量的荒谬是出在输入输出的边际价上。大家还拿地方的例子,三个输入框需求输入1-一千0里边的数。我们要测它有没有超过那一个范围,如:0、-壹 、-二 、1000、一千1…..等等,来判定是不是高于了我们的限量。

    3.  因果图

      
因果图方法最平生成的就是判断表,它适合于检查程序输入条件的种种组合景况。举个例子:原因:A=0,B=0,结果本身就可以看清:A=B。确切的说他是一种因果关系考虑。它会无意识引导那大家的测试。当然了,大家为了免于遗漏,可以把系统中的因果关系用画图出。但是系统大而复杂的话就是个体力活了。呵呵。

    4.  错误预计法

     基于经验和直觉预计出系统只怕存在的错误,从而有针对的筹划测试用例的法门。

   5.  其它

     
设计测试用例的格局有不少,我们常用就地点二种,别的的方法还有:状态迁移图、流程分析法、正交验证法等等。

 

④ 、测试用例的格式与要素

   1个测试用例应该包罗:编号,标题,测试场景,测试步骤,预期结果。

   当然还可参加一些它采纳,如:优先级、测试阶段….

 

注:上边的格式取自《微软的软件测试之道》,它并不一定适合您,作者只是让我们对测试格式有个了解。

关于测试用例的寄放保管:

1. 
体系管理连串自带的用例管理,一般用例会与类型挂钩,有稳定的格式,搜索、修改等效果,使用起来卓殊便宜。如:禅道项目管理、QC、bugfree
等等都带的有用例管理职能。

2.  通过world\Excel文档方式管理,那样的裨益就是协调定义测试用例的格式。

———————–测试用例例子——————————————————–

基础知识了解的大半了,上边来看3个现实的测试用例。大家会有更深厚的认识。

 

 注:那不是一个全部的测试用例,格式也不是永恒必须这么的,你们可以根据自个儿的急需编制设计测试用例。

==========================================================================

————————————我们还亟需知道的,关于测试用例的——————————-

1、.我们在什么样时候能够设计测试用例?

   
当按照客户的要求整理出档次需要分析文档时,大家就可以依照须要文档来编排测试用例了。然而,一般大家(国内大多小公司)项目需要文档都十二分“简陋”,所以,很难依照须要文档设计测试用例。

   
大家唯有等到品种开发人士把品种支出出来,给大家系统文档、陈设环境、数据库结构(若是系统牵涉到数据库的话),大家根绝那几个文档来规划测试用例。

贰 、测试用例的评审与创新

    
大家安插的测试用例设计成就之后,是还是不是完好?是还是不是切合系统?符合客户需要?对用例做三个评审是不可或缺。关于评审的不二法门,差其余店堂有区其余流程。

    
我们编辑的测试用例也不是通过评审之后就不变了,随着需求的转移、作用的改正,测试用例当然也需求创新和更改。

三 、什么状态下不吻合写测试用例

  •      文件时间

      
即便贰个功效小编火速就测试完了,而且只须求测试四次,但我们布署测试用例时却比较费心,花时间也长。这么些时候就没要求编写测试用例了。

  •      需要变动大且频仍

     
必要的功用改变相当频仍,而且转移很大,此前编写的测试用例根本没办法使用,必要求重复编辑,这么些时候也没要求去设计测试用例了。

  •      项目时间不一样意

     
这一项是不太宽厚的做法,即便不是亟需交付客户来说,尽量不要那样做;当然了,借使只是给客户出示或试用,可以在其后进行补充和周详测试用例。

  • 毫无编写不完全或外人看不懂的测试用例,那样就从未意思了。

============个人闲谈内容,欢迎指正========

肆 、截至软件测试的正儿八经。

      语句覆盖最低不可以小于十分八,测试须要覆盖率达到百分之百,测试用例覆盖率达到百分百,① 、二级缺陷修复率达到百分百,三 、四级修复率达到百分之八十。

      (上面一句是再网上找的,不是业内,只是个参考)

      bug等级:

      超级:非凡严重的bug

      二级:严重的bug

      三级:一般性的bug

      四级:提议性难点

伍 、关于探索性测试

      
完全的执行测试用例时一件拾叁分枯燥的事体,个人在推行测试用例时会做一些,别的的十三分规性的操作,看系统是还是不是会有照应的拍卖和提示。小编的一有个别bug就是再那种越发操作下发现的。

      
当然了确实的批判性测试须要对成品的浓密摸底,以及软件开发技术有自然的吃水和幅度。姑且把大家的批判性测试看成是瞎捣鼓吧!呵呵。

陆 、 交叉测试

     有木有发现,当大家率先遍测试系统时,会尤其认真,但要我们测试第②遍时,大家不愿意像第两遍这样认真的去测了,那没办法印证大家不负责,而是逐个人都有些心理现象。那么些时候,我们得以和其它测试职员沟通功效来测试,提升作用,而且更便于发现标题。

柒 、测试的目标

    1.  大家让它做的它必须会做。

   2.  我们不让它做的它必须不会做。

   或者您会发觉有增大功能的时候,就是客户没有要求,大家加了如此的机能,或然加了那一点效率种类看上去会更好。那时怎么办?算难点么?

  
作为开发人士,中规中矩的做东西最好,假若实在有相当好的效果要加的话,须求和客户互换,然后写到需要里。终归多一点作用多一点风险。呵呵

   作为测试人士,凡是不切合须求文档的都急需当难题点提出。权利肯定,防止持续麻烦。   


 修改:

 1.测试用例的格式的要素,去掉“实际结果”

 2.关于测试方法“等价类划分”的表达。