首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 更多程式語言 >> 更多程式設計知識

Flask 應用導入單元測試與錯誤處理實戰

     編輯:更多程式設計知識

隨著專案規模增長,手動測試已無法覆蓋所有情境。本文示範如何在 Flask 專案中建立單元測試框架、客製化 HTTP 錯誤頁面、設定錯誤日誌郵寄與檔案記錄,並修復一個昵稱唯一性的實際 Bug。

發現 Bug:昵稱唯一性未驗證

原始碼在 OpenID 回調與編輯資料表單皆未檢查昵稱重複,導致資料庫拋出 IntegrityError。在開發模式下會顯示堆疊追蹤,生產模式則回傳 500 錯誤頁面。

關閉 Debug 模式與客製錯誤頁面

建立 runp.py 關閉 debug:

#!flask/bin/python
from app import app
app.run(debug=False)

在 app/views.py 註冊錯誤處理器:

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

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

對應模板 404.html、500.html 繼承 base.html,提供友善提示與返回連結。

錯誤日誌郵寄與檔案記錄

在 config.py 設定郵件伺服器與管理員:

MAIL_SERVER = 'localhost'
MAIL_PORT = 25
ADMINS = ['admin@example.com']

在 app/__init__.py 初始化 SMTPHandler 與 RotatingFileHandler:

if not app.debug:
    import logging
    from logging.handlers import SMTPHandler, RotatingFileHandler

    # 郵件錯誤通知
    credentials = (MAIL_USERNAME, MAIL_PASSWORD) if MAIL_USERNAME or MAIL_PASSWORD else None
    mail_handler = SMTPHandler((MAIL_SERVER, MAIL_PORT),
                               'no-reply@' + MAIL_SERVER,
                               ADMINS, 'Microblog Failure', credentials)
    mail_handler.setLevel(logging.ERROR)
    app.logger.addHandler(mail_handler)

    # 檔案日誌
    file_handler = RotatingFileHandler('tmp/microblog.log', 'a', 1*1024*1024, 10)
    file_handler.setFormatter(logging.Formatter(
        '%(asctime)s %(levelname)s: %(message)s [in %(pathname)s:%(lineno)d]'))
    app.logger.setLevel(logging.INFO)
    file_handler.setLevel(logging.INFO)
    app.logger.addHandler(file_handler)
    app.logger.info('Microblog startup')

修復昵稱重複 Bug

1. OpenID 回調自動生成唯一昵稱

@staticmethod
def make_unique_nickname(nickname):
    if User.query.filter_by(nickname=nickname).first() is None:
        return nickname
    version = 2
    while True:
        new_nickname = nickname + str(version)
        if User.query.filter_by(nickname=new_nickname).first() is None:
            break
        version += 1
    return new_nickname

在 after_login 中呼叫:

nickname = User.make_unique_nickname(nickname)

2. 編輯表單加入驗證

class EditForm(Form):
    nickname = TextField('nickname', validators=[Required()])
    about_me = TextAreaField('about_me', validators=[Length(min=0, max=140)])

    def __init__(self, original_nickname, *args, **kwargs):
        Form.__init__(self, *args, **kwargs)
        self.original_nickname = original_nickname

    def validate(self):
        if not Form.validate(self):
            return False
        if self.nickname.data == self.original_nickname:
            return True
        user = User.query.filter_by(nickname=self.nickname.data).first()
        if user is not None:
            self.nickname.errors.append('This nickname is already in use. Please choose another one.')
            return False
        return True

視圖函數傳入原始昵稱:form = EditForm(g.user.nickname)。模板顯示錯誤訊息。

建立單元測試框架

使用 Python 內建 unittest,在 tests.py 撰寫測試案例:

import unittest
import os
from config import basedir
from app import app, db
from app.models import User

class TestCase(unittest.TestCase):
    def setUp(self):
        app.config['TESTING'] = True
        app.config['CSRF_ENABLED'] = False
        app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir, 'test.db')
        self.app = app.test_client()
        db.create_all()

    def tearDown(self):
        db.session.remove()
        db.drop_all()

    def test_avatar(self):
        u = User(nickname='john', email='john@example.com')
        avatar = u.avatar(128)
        expected = 'https://www.gravatar.com/avatar/d4c74594d841139328695756648b6bd6'
        assert avatar[:len(expected)] == expected

    def test_make_unique_nickname(self):
        u = User(nickname='john', email='john@example.com')
        db.session.add(u)
        db.session.commit()
        nickname = User.make_unique_nickname('john')
        assert nickname != 'john'
        u2 = User(nickname=nickname, email='susan@example.com')
        db.session.add(u2)
        db.session.commit()
        nickname2 = User.make_unique_nickname('john')
        assert nickname2 != 'john'
        assert nickname2 != nickname

if __name__ == '__main__':
    unittest.main()

執行 ./tests.py 即可跑測試套件。測試涵蓋 Gravatar 網址生成與昵稱唯一化邏輯,確保重構後功能正常。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved