如何测试自定义Flask错误页面?

9 投票
3 回答
4966 浏览
提问于 2025-04-18 17:42

我正在尝试在Flask中测试一个自定义错误页面(这里是404页面)。

我定义了我的自定义404页面,代码如下:

@app.errorhandler(404)
def page_not_found(e):
    print "Custom 404!"
    return render_template('404.html'), 404

当我在浏览器中访问一个未知页面时,这个自定义页面工作得很好(我在输出中看到了Custom 404!,而且我的自定义内容也能正常显示)。但是,当我通过unittestnose来触发404错误时,却显示了标准的服务器404页面。我没有看到任何日志消息,也没有看到我想测试的自定义内容。

我的测试案例是这样定义的:

class MyTestCase(TestCase):
    def setUp(self):
        self.app = create_app()
        self.app_context = self.app.app_context()
        self.app.config.from_object('config.TestConfiguration')
        self.app.debug = False # being explicit to debug what's going on...
        self.app_context.push()
        self.client = self.app.test_client()

    def tearDown(self):
        self.app_context.pop()

    def test_custom_404(self):
        path = '/non_existent_endpoint'
        response = self.client.get(path)
        self.assertEqual(response.status_code, 404)
        self.assertIn(path, response.data)

我在测试应用中明确将app.debug设置为False。我还需要设置其他什么吗?

3 个回答

0

关于你提到的那句话:“如果我把错误处理器直接放到create_app()方法里,一切都能正常工作……但感觉有点别扭?也许?”:

你可以定义一个函数来注册错误处理器,然后在你的create_app函数里调用这个函数:

def create_app():
    app = Flask(__name__)
    app.config.from_object('config.BaseConfiguration')
    app.secret_key = app.config.get('SECRET_KEY')
    app.register_blueprint(main.bp)
    register_error_pages(app)
    return app

app = create_app()

# Custom error pages

def register_error_pages(app):
    @app.errorhandler(404)
    def page_not_found(e):
        return render_template('404.html'), 404

这样,如果你还有其他自定义的错误处理器(比如403、405、500错误),你就可以在register_error_pages函数里定义它们,而不是在create_app函数里。

1

Flask 应用程序对象有一个叫做 error_handler_spec 的属性,可以用来解决这个问题:

这个属性是一个字典,里面记录了所有注册的错误处理器。字典的键为 None 时,表示这个错误处理器是针对整个应用的;如果键是其他值,那就是某个蓝图的名字。每个键对应的值又是一个字典,这个字典的键是 HTTP 异常的状态码。特殊的键 None 对应的是一个元组列表,元组的第一个元素是用于实例检查的类,第二个元素是错误处理函数。

所以在你的测试方法中,可以这样做:

mock_page_not_found = mock.magicMock()  
mock_page_not_found.return_value = {}, 404

with mock.patch.dict(self.app.error_handler_spec[None], {404: mock_page_not_found}):
  path = '/non_existent_endpoint'
  response = self.client.get(path)

  self.assertEqual(response.status_code, 404)
  mock_page_not_found.assert_called_once()
9

经过重新审视,我发现问题出在我应用的初始化上,而不是我的测试或配置上。我的应用的 __init__.py 文件基本上是这样的:

def create_app():
    app = Flask(__name__)
    app.config.from_object('config.BaseConfiguration')
    app.secret_key = app.config.get('SECRET_KEY')
    app.register_blueprint(main.bp)
    return app

app = create_app()

# Custom error pages

@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'), 404

注意,错误处理器是附加在 @app 上的,而不是在我在 TestCase.setUp() 方法中调用的 create_app() 方法里。

如果我把这个错误处理器移动到 create_app() 方法里,一切就正常了……但这样做感觉有点别扭?也许吧?

def create_app():
    app = Flask(__name__)
    app.config.from_object('config.BaseConfiguration')
    app.secret_key = app.config.get('SECRET_KEY')
    app.register_blueprint(main.bp)

    # Custom error pages
    @app.errorhandler(404)
    def page_not_found(e):
        return render_template('404.html'), 404

    return app

这最终解决了我的问题,但我还是想听听其他人对如何以不同方式注册这些错误处理器的看法。

撰写回答