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

9

我正在尝试在Flask中测试自定义错误页面(在此例中为404)。

我已将自定义的404页面定义为:

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

当在浏览器中访问未知页面时,这个功能完美地运行(我可以在stdout中看到“Custom 404!”并且我的自定义内容可见)。然而,当尝试使用nose通过unittest触发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。我还需要明确设置其他内容吗?


请问您能否发布您的单元测试代码? - Yuvika
你必须明确将TESTING设置为True。这还取决于您如何设置日志记录器。 - corvid
我们需要看到您测试命中一个端点的情况。 - Joe Doherty
3个回答

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 外部的 create_app() 方法中,在我的 TestCase.setUp() 方法中调用。
如果我将该错误处理程序简单地移到 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

这个答案最终解决了我的问题,但我很想听听有关如何以不同的方式注册这些错误处理程序的其他想法。


1
在您的MyTestCase类中,在调用create_app之后,您还可以执行self.app.register_error_handler(404, page_not_found)(您还需要单独在测试文件中导入该方法)。这样,您就可以保持原始文件不变,但是您需要在测试文件中做两行额外的代码(一个用于导入,一个用于register_error_handler)......我也觉得这不是一个完美的解决方案,因为flask_test_client()应该自动执行此操作......最好的解决方案可能是通过super覆盖该方法,并专门注册任何使用errorhandler修饰符的方法。 - Jon
为什么不专门为错误创建一个蓝图,然后您只需要将该蓝图注册到您的应用程序中呢? - Anthony Rolfe
对于404错误,请访问您的应用程序网址/不在我的代码中的页面。 - myrccar

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()

不确定是否有API更改,但对我来说,类似于from werkzeug.exceptions import BadRequest然后{400: {BadRequest: mock_bad_request}}这样的代码可以工作。 - Patrick Mutuku

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函数中定义。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接