Testing with Flask is generally straightforward but some things aren’t trivial. The Flask-Testing extension helps us with some really useful assertions like
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
view without rendering the template we can’t because we are making assertions in the generated document.
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
And now my test looks like this.
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.