Flask 教程,第七部分:单元测试 已翻译 100%

oschina 投递于 2012/12/28 09:30 (共 26 段, 翻译完成于 03-10)
阅读 10325
收藏 6
3
加载中

这是我用 Python 的轻量级框架 Flask 编写web程序时的一些经验,我将它记录在此,本篇是一系列文章中的第七篇。

这系列教程的目的是开发一个功能完善的,但没有创意的微博应用,我决定称之为microblog。

下面是这个系列中已经发布的文章的目录:

fonoisrev
翻译于 2013/01/07 19:32
4

概要

在前面的章节里我们专注于在我们的小应用程序上一步步的添加功能上。到现在为止我们有了一个带有数据库的应用程序,可以注册用户,记录用户登陆退出日志以及查看修改配置文件。

在本节中,我们不为应用程序添加任何新功能,相反,我们要寻找一种方法来增加我们已写代码的稳定性,我们还将创建一个测试框架来帮助我们防止将来程序中出现的失败和回滚。

缪斯的情人
翻译于 2013/03/07 20:46
2

让我们来找bug

在上一章的结尾谈到,我故意在应用程序中引入一个bug。接下来让我描述一下它是什么样的bug,然后看看当我们的程序不按照我们意愿执行的时候,它在其中又起了什么样的影响。

应用程序的问题在于,没有保证用户昵称的唯一性。用户昵称是由应用程序自动初始化的。我们首先会考虑使用OpenID provider给出的用户的昵称,然后再考虑使用Email信息中的用户名部分作为用户的昵称。但如果出现重复的昵称,则后面的用户将无法注册成功。更糟糕的是,在修改用户配置的表单中,我们允许用户任意更改他们的昵称,但我们仍然没有对昵称冲突进行检查。

当我们分析完错误产生时应用程序的行为之后,我们将会定位这些问题。

fonoisrev
翻译于 2013/01/07 10:17
2

Flask 的调试功能

那么让我们看看当bug被触发时,会出现什么现象。

让我们从创建一个崭新的数据库,在linux下,执行:

rm app.db
./db_create.py

在Windows下,执行:

del app.db
flask/Scripts/python db_create.py

我们需要两个OpenID的账号来重现这个bug。当然这两个账号最理想的状态是来自来个不同的拥有者,那样可以避免他们的cookie把情况搞的更复杂。通过如下步骤创建冲突的昵称:

  • 用第一个账号登陆
  • 进入用户信息属性编辑页面,将昵称改为“dup”
  • 登出系统
  • 用第二个账号登陆
  • 修改第二个账号的用户信息属性,将昵称改为“dup”
fonoisrev
翻译于 2013/01/07 18:59
4
哎哟!sqlalchemy中抛出了一个异常,来看一下错误信息:
sqlalchemy.exc.IntegrityError
IntegrityError: (IntegrityError) column nickname is not unique u'UPDATE user SET nickname=?, about_me=? WHERE user.id = ?' (u'dup', u'', 2)

错误的后面是这个错误的堆栈信息,事实上,这是一个相当不错的错误提示,你可以转向任何框架检查代码或者在浏览器里执行正确的表达式。

这个错误信息相当明确,我们试图在数据插入一个重复的昵称,数据库的昵称字段是一个卫衣键,因此这样的操作是无效的。

缪斯的情人
翻译于 2013/03/07 20:56
2

除了实际的错误,在我们手头上还有一个次要的错误。如果一个用户不注意在我们应用程序里引起了一个错误(这一个错误或者任何其他原因引起的异常),应用程序将向他/她暴漏错误信息和堆栈信息,而不是暴露给我们。对于我们开发者来说这是个很好的特性,但是很多时候我们不想让用户看到这些信息。

这么长时间以来,我们一直在debug模式下运行我们的应用程序,我们通过设置debug=True的参数来启用应用程序的debug模式。这里我们在运行脚本run.py里配置。

缪斯的情人
翻译于 2013/03/07 21:08
2

当我们这样开发应用是方便的,但是我们需要在生产环境上关闭debug模式。 让我们创建另一个启动脚本文件设置关闭dubug模式(filerunp.py): 

#!flask/bin/python
from app import app
app.run(debug = False)
现在重新启动应用:
  ./runp.py

并且现在再尝试重命名第二个账号nickname成‘dup’

lidashuang
翻译于 2013/02/28 21:30
2

这次我们没有获取到一个错误信息,取而代之,我们得到了一个HTTP 500错误码,这是个内部服务器错误。虽然这不容易定位错误,但至少没有暴露我们应用程序的任何细节给陌生人。当调试关闭后出现一个异常时,Flask会产生一个500页面。

虽然这样好些了,但现在仍存在两个问题。首先美化问题:默认的500页面很丑陋。第二个问题更重要些,当用户操作失败时,我们无法获取到错误信息了,因为错误在后台默默的处理了。幸运的是有个简单方式来处理这两个问题。

缪斯的情人
翻译于 2013/03/07 21:19
2

定制HTTP错误处理程序

Flask为应用程序提供了一个机制来安装他们自己的错误页面,作为例子,让我们定义两个最常见的HTTP 404和500错误的自定义页面。定制其他错误页面也是同样的方式。

使用一个修饰来声明一个定制的错误处理程序 (fileapp/views.py):

@app.errorhandler(404)
def internal_error(error):
    return render_template('404.html'), 404

@app.errorhandler(500)
def internal_error(error):
    db.session.rollback()
    return render_template('500.html'), 500

这地方无需多言,因为他们都是不言而喻的。唯一有趣的地方时错误500处理中的rollack语句,这个地方是不可缺少的因为这个方法会被当做一个异常调用。如果因为数据库错误导致一个异常,那么数据库的会话将变成一个无效状态,因此我们需要回滚它,以防止一个会话转向一个500错误的模板。

缪斯的情人
翻译于 2013/03/07 21:31
2

这是一个404错误在模版

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<h1>File Not Found</h1>
<p><a href="{{url_for('index')}}">Back</a></p>
{% endblock %}

这是一个500错误的模版

<!-- extend base layout -->
{% extends "base.html" %}

{% block content %}
<h1>An unexpected error has occurred</h1>
<p>The administrator has been notified. Sorry for the inconvenience!</p>
<p><a href="{{url_for('index')}}">Back</a></p>
{% endblock %}

注意,我们会继续使用我们base.html 布局, 这样我们的错误页看起来比较舒服

lidashuang
翻译于 2013/02/28 21:34
1
本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接。
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。
加载中

评论(1)

likeyi
likeyi
不错,这一章通关,继续下一章。
返回顶部
顶部