敏捷开发(agile development)
scrum
结对编程
测试驱动开发(TDD): Test driven development
单元测试(unit testing)是开发者自己编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确。通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。
注意单元测试是开发人员自己负责
unittest
Pytest是 python的一种unittest框架,与python自带的unittest测试框架类似,但是比unittest框架使用起来更简洁,效率更高。
pytest fixture用途
1.做测试前后的初始化设置,如测试数据准备,链接数据库,打开浏览器等这些操作都可以使用fixture来实现
2.测试用例的前置条件可以使用fixture实现
3.支持经典的xunit fixture ,像unittest使用的setup和teardown
4.fixture可以实现unittest不能实现的功能,比如unittest中的测试用例和测试用例之间是无法传递参数和数据的,但是fixture却可以解决这个问题
安装pytest-flask扩展
pip install pytest-flask
Dev - test - demo - product
为了测试时方便传入不同的配置参数 (如使用不同的数据库和其他参数),我们需要使用工厂模式创建flask的app对象,改造代码如下
1、创建school/settings.py文件,统一放置项目配置参数
SECRET_KEY = 'q234asdfad@#$AdfS*UNFs'
# 设置数据库连接字符串
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root:Vff123456@127.0.0.1/mouse?charset=UTF8MB4'
# 不跟踪修改,不设置会有警告
SQLALCHEMY_TRACK_MODIFICATIONS = False
# 每页条数
COUNT_PER_PAGE = 10
2、修改application/init.py文件,加入create_app方法
#application/__init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
# 初始化LoginManager
login_manager = LoginManager()
# 设为 'strong' 时,Flask-Login 会记录客户端 IP 地址和浏览器的用户代理信息,如果发现异动就登出用户
login_manager.session_protection = 'strong'
# login_view 属性设置登录页面
login_manager.login_view = 'users.login'
# 定制未授权访问提示信息
login_manager.login_message = '访问该功能需要登录'
# 创建数据库连接
db = SQLAlchemy()
def create_app(config=None):
app = Flask(__name__)
if config is not None:
app.config.from_object(config)
from application.home.views import home
from application.users.views import users
# 注册蓝图
app.register_blueprint(home, url_prefix='')
app.register_blueprint(users, url_prefix='/users')
db.init_app(app)
login_manager.init_app(app)
return app
3、修改manager.py文件
from application import create_app, db
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
if __name__ == '__main__':
app = create_app('settings')
manager = Manager(app)
# 数据库迁移相关
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)
manager.run()
1.conftest.py文件名字是固定的,不可以做任何修改
2.文件和用例文件在同一个目录下,那么conftest.py作用于整个目录
3.conftest.py文件不能被其他文件导入
4.所有同目录测试文件运行前都会执行conftest.py文件
如果我们在编写测试用例的时候,每一个测试文件里面的用例都需要先登录后才能完成后面的操作,那么们该如何实现呢?这就需要我们掌握conftest.py文件的使用了
# /school/conftest.py
import pytest
from application import create_app, db
@pytest.fixture
def app():
app = create_app('test_settings')
return app
创建_school_tests/test_application.py
import flask
def test_app(app):
assert isinstance(app, flask.Flask)
执行用例 (在school目录下)
$ python -m pytest tests
# conftest.py
# session表示该fixture作用于整个测试过程,只创建一次
@pytest.fixture(scope='session')
def db(app, request):
database.app = app
database.create_all()
def teardown():
database.drop_all()
request.addfinalizer(teardown)
return database
# function表示该fixture会在每个测试方法都执行
@pytest.fixture(scope='function')
def session(db, request):
session = db.create_scoped_session()
db.session = session
def teardown():
session.remove()
request.addfinalizer(teardown)
return session
创建model测试用例_school_tests/test_user_model.py
from application.users.models import Users, Class
def test_create_user(session):
username = 'carmack'
fullname = '肖世荣'
password = 'ASDFAD@#23232'
user = Users(username=username, fullname=fullname, password=password)
session.add(user)
session.commit()
assert user.id is not None
assert Users.query.count() == 1
assert user.status == 1
assert user.vote_type == 'go'
assert user.get_vote_button() == 'stop'
assert user.get_vote_color() == 'green'
# 测试user和class关联关系
def test_user_class_relation(session):
user1 = Users(username='jungle', fullname='林林', password='23434213sSWE')
user2 = Users(username='ken', fullname='郑冠', password='2343asSWE')
c1 = Class(name='python1904')
c2 = Class(name='python1905')
user1.uclass = c1
user2.uclass = c1
assert len(c1.users) == 2
assert c1.users[0].username == 'jungle'
session.add_all([user1, user2, c1, c2])
session.commit()
c1_obj = Class.query.filter_by(name='python1904').first()
assert c1_obj is not None
assert len(c1_obj.users) == 2
from application.users.models import Users
from flask import get_flashed_messages
from werkzeug.security import check_password_hash
import pytest
import sqlalchemy
# 测试成功的注册
# client 是Werkzeug自带的测试客户端
def test_valid_register(client):
# 测试get请求下register页面是否正确
response = client.get('/users/register')
assert response.status_code == 200
assert '欢迎注册' in bytes.decode(response.data)
# 测试post请求
data = {'username': 'morning', 'fullname': '黎明', 'password': '123456'}
response = client.post('/users/register', data=data)
assert response.status_code == 302
assert len(get_flashed_messages()) == 1
assert '注册成功' in get_flashed_messages()[0]
user = Users.query.filter_by(username=data['username']).one()
assert user.fullname == data['fullname']
assert user.password
assert check_password_hash(user.password, data['password'])
# 注册失败的条件更需要测试
# 测试不满足条件的注册
def test_invalid_register(client):
response = client.get('/users/register')
data = {'username': ' ', 'fullname': '黎明' * 200, 'password': '123456'}
response = client.post('/users/register', data=data)
assert response.status_code == 200
assert '用户名必填' in bytes.decode(response.data)
assert '姓名不能大于128位' in bytes.decode(response.data)
# 测试抛异常情况
with pytest.raises(sqlalchemy.orm.exc.NoResultFound):
Users.query.filter_by(username=data['username']).one()