Testing with Flask is generally
straightforward but some things aren’t
trivial. The Flask-Testing extension helps us with some really useful
TestCase#assert_template_used but it miss a way to test flash messages.
One of the ways to test flashes is asserting if the message is in the response data.
Suppose the production code.
@app.route('/post/') def new_post(self): if storage.create_post(request.form['post']): flash(u'Post added') return redirect(url_for('home')) return render_template('new-post.html')
And the test code.
def should_flash_a_success_message(self): response = self.client.post('/post/', data=self.valid_post_data, follow_redirects=True) assert 'Post added' in response.data
This works but personally I don’t like it. One reason is we need to
follow the redirect and this can lead to testing the same thing twice
because we should have a test for the
home route already.
The other reason is related to isolation. If we want to test the
rendering the template we can’t because we are making assertions in the
Railsby default doesn’t render any view in controller specs. In
Flask-Testingit renders but you can turn off template rendering.
Another way to test flash messages is using a
mock. You replace the
flash method with a
mock and use expectations to assert flash
calls. This works but I like to avoid mocks when possible.
My solution consist in defining a new method to do the job. I recommend you to define it in a base test class so you can reuse it.
from flask.ext.testing import TestCase import myapp class BaseTest(TestCase): def create_app(self): return myapp.create_app(TESTING=True) def assert_flashes(self, expected_message, expected_category='message'): with self.client.session_transaction() as session: try: category, message = session['_flashes'] except KeyError: raise AssertionError('nothing flashed') assert expected_message in message assert expected_category == category
def should_flash_a_success_message(self): self.client.post('/post/', data=self.valid_post_data) self.assert_flashes('Post added')
Don’t forget to subclass BaseTest in your test classes. Another trick
thing about flashes is that they need the session to work and sessions
doesn’t work without the
SECRET_KEY set in Flask so be sure you set it
before running the tests.