Permalink
Browse files

Beefed up the tutorial

  • Loading branch information...
1 parent 1246f40 commit 6dd92ae4b32c336564231e10db12458a8b9261ca @mitsuhiko mitsuhiko committed Apr 16, 2010
View
@@ -40,6 +40,8 @@ So here a simple example how you can use SQLite 3 with Flask::
g.db.close()
return response
+.. _easy-querying:
+
Easy Querying
`````````````
View
@@ -89,6 +89,14 @@ Or pass it to run::
Both will have exactly the same effect.
+.. admonition:: Attention
+
+ The interactive debugger however does not work in forking environments
+ which makes it nearly impossible to use on production servers but the
+ debugger still allows the execution of arbitrary code which makes it a
+ major security risk and **must never be used on production machines**
+ because of that.
+
Routing
-------
View
@@ -22,95 +22,166 @@ installation.
The Application
---------------
-First we need an application to test for functionality. Let's start
-simple with a Hello World application (`hello.py`)::
+First we need an application to test for functionality. For the testing
+we will use the application from the :ref:`tutorial`. If you don't have
+that application yet, get the sources from `the examples`_.
- from flask import Flask, render_template_string
- app = Flask(__name__)
-
- @app.route('/')
- @app.route('/<name>')
- def hello(name='World'):
- return render_template_string('''
- <!doctype html>
- <title>Hello {{ name }}!</title>
- <h1>Hello {{ name }}!</h1>
- ''', name=name)
+.. _the examples:
+ http://github.com/mitsuhiko/flask/tree/master/examples/flaskr/
The Testing Skeleton
--------------------
In order to test that, we add a second module (
-`hello_tests.py`) and create a unittest skeleton there::
+`flaskr_tests.py`) and create a unittest skeleton there::
import unittest
- import hello
+ import flaskr
@gabrielfalcao
gabrielfalcao Jul 21, 2015

where is this flaskr from ?

@untitaker
untitaker Jul 21, 2015 Member

It's the tutorial.

+ import tempfile
- class HelloWorldTestCase(unittest.TestCase):
+ class FlaskrTestCase(unittest.TestCase):
def setUp(self):
- self.app = hello.app.test_client()
+ self.db = tempfile.NamedTemporaryFile()
+ self.app = flaskr.app.test_client()
+ flaskr.DATABASE = self.db.name
+ flaskr.init_db()
if __name__ == '__main__':
unittest.main()
-The code in the `setUp` function creates a new test client. That function
-is called before each individual test function. What the test client does
-for us is giving us a simple interface to the application. We can trigger
-test requests to the application and the client will also keep track of
-cookies for us.
+The code in the `setUp` function creates a new test client and initialize
+a new database. That function is called before each individual test function.
+What the test client does for us is giving us a simple interface to the
+application. We can trigger test requests to the application and the
+client will also keep track of cookies for us.
+
+Because SQLite3 is filesystem based we can easily use the tempfile module
+to create a temporary database and initialize it. Just make sure that you
+keep a reference to the :class:`~tempfile.NamedTemporaryFile` around (we
+store it as `self.db` because of that) so that the garbage collector does
+not remove that object and with it the database from the filesystem.
If we now run that testsuite, we should see the following output::
- $ python hello_tests.py
+ $ python flaskr_tests.py
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
-Even though it did not run any tests, we already know that our hello
+Even though it did not run any tests, we already know that our flaskr
application is syntactically valid, otherwise the import would have died
with an exception.
The First Test
--------------
-Now we can add the first test. Let's check that the application greets us
-with "Hello World" if we access it on ``/``. For that we modify our
-created test case class so that it looks like this::
+Now we can add the first test. Let's check that the application shows
+"No entries here so far" if we access the root of the application (``/``).
+For that we modify our created test case class so that it looks like
+this::
- class HelloWorldTestCase(unittest.TestCase):
+ class FlaskrTestCase(unittest.TestCase):
def setUp(self):
- self.app = hello.app.test_client()
+ self.db = tempfile.NamedTemporaryFile()
+ self.app = flaskr.app.test_client()
+ flaskr.DATABASE = self.db.name
+ flaskr.init_db()
- def test_hello_world(self):
+ def test_empty_db(self):
rv = self.app.get('/')
- assert 'Hello World!' in rv.data
+ assert 'No entries here so far' in rv.data
Test functions begin with the word `test`. Every function named like that
will be picked up automatically. By using `self.app.get` we can send an
HTTP `GET` request to the application with the given path. The return
value will be a :class:`~flask.Flask.response_class` object. We can now
use the :attr:`~werkzeug.BaseResponse.data` attribute to inspect the
return value (as string) from the application. In this case, we ensure
-that ``'Hello World!'`` is part of the output.
+that ``'No entries here so far'`` is part of the output.
+
+Run it again and you should see one passing test::
+
+ $ python flaskr_tests.py
+ .
+ ----------------------------------------------------------------------
+ Ran 1 test in 0.034s
+
+ OK
+
+Of course you can submit forms with the test client as well which we will
+use now to log our user in.
+
+Logging In and Out
+------------------
+
+The majority of the functionality of our application is only available for
+the administration user. So we need a way to log our test client into the
+application and out of it again. For that we fire some requests to the
+login and logout pages with the required form data (username and
+password). Because the login and logout pages redirect, we tell the
+client to `follow_redirects`.
+
+Add the following two methods do your `FlaskrTestCase` class::
+
+ def login(self, username, password):
+ return self.app.post('/login', data=dict(
+ username=username,
+ password=password
+ ), follow_redirects=True)
-Run it again and you should see one passing test. Let's add a second test
-here::
+ def logout(self):
+ return self.app.get('/logout', follow_redirects=True)
- def test_hello_name(self):
- rv = self.app.get('/Peter')
- assert 'Hello Peter!' in rv.data
+Now we can easily test if logging in and out works and that it fails with
+invalid credentials. Add this as new test to the class::
+
+ def test_login_logout(self):
+ rv = self.login(flaskr.USERNAME, flaskr.PASSWORD)
+ assert 'You were logged in' in rv.data
+ rv = self.logout()
+ assert 'You were logged out' in rv.data
+ rv = self.login(flaskr.USERNAME + 'x', flaskr.PASSWORD)
+ assert 'Invalid username' in rv.data
+ rv = self.login(flaskr.USERNAME, flaskr.PASSWORD + 'x')
+ assert 'Invalid password' in rv.data
+
+Test Adding Messages
+--------------------
+
+Now we can also test that adding messages works. Add a new test method
+like this::
+
+ def test_messages(self):
+ self.login(flaskr.USERNAME, flaskr.PASSWORD)
+ rv = self.app.post('/add', data=dict(
+ title='<Hello>',
+ text='<strong>HTML</strong> allowed here'
+ ), follow_redirects=True)
+ assert 'No entries here so far' not in rv.data
+ self.login(flaskr.USERNAME, flaskr.PASSWORD)
+ assert '&lt;Hello&gt' in rv.data
+ assert '<strong>HTML</strong> allowed here' in rv.data
+
+Here we also check that HTML is allowed in the text but not in the title
+which is the intended behavior.
+
+Running that should now give us three passing tests::
+
+ $ python flaskr_tests.py
+ ...
+ ----------------------------------------------------------------------
+ Ran 3 tests in 0.332s
+
+ OK
-Of course you can submit forms with the test client as well. For that and
-other features of the test client, check the documentation of the Werkzeug
-test :class:`~werkzeug.Client` and the tests of the MiniTwit example
-application:
+For more complex tests with headers and status codes, check out the
+`MiniTwit Example`_ from the sources. That one contains a larger test
+suite.
-- Werkzeug Test :class:`~werkzeug.Client`
-- `MiniTwit Example`_
.. _MiniTwit Example:
http://github.com/mitsuhiko/flask/tree/master/examples/minitwit/
Oops, something went wrong.

0 comments on commit 6dd92ae

Please sign in to comment.