2014-01-28

Flask TodoMVC: Configuration

This is the fourth article in the Flask TodoMVC tutorial, a series that creates a Backbone.js backend with Flask for the TodoMVC app. In this article, we will setup unit testing and demonstrate custom configuration used during testing. We do this in preparation for the next article, where we will create unit tests against the todo api.

Previous articles in this series:

  1. Getting Started
  2. Server Side Backbone Sync
  3. Dataset Persistence

We will begin with where we left off in the previous article. If you would like to follow along, but do not have the code, it is available on GitHub.

# Optional, use your own code if you followed the previous article
$ git clone https://github.com/kevinbeaty/flask-todomvc
$ cd flask-todomvc
$ git checkout -b config dataset

Let's get started.

Custom Configuration

We would like to create tests that run against a test database without affecting our actual todo list. If you lookup server.py, however, you may notice that we have a problem. The database connects before we have a chance to change the database URL.

db = dataset.connect('sqlite:///todos.db')
todos = db['todos']

app = Flask(__name__, static_url_path='')
app.debug = True

We need to allow changing the database URL to a temporary file during testing. To do this, we are going to use default configuration within server.py, but allow overriding the settings by loading from a file specified by an environment variable.

Change the above code to the following:

app = Flask(__name__, static_url_path='')

app.config.from_object('config.default')
app.config.from_envvar('TODO_SETTINGS', silent=True)

db = dataset.connect(app.config['DATABASE'])
todos = db['todos']

After we create the app, we load the configuration with default parameters from config/default.py using from_object. We then call from_envvar to allow overriding the default configuration with custom values (more later). We pass silent=True so it will not complain if we don't use an environment variable, such as when we run server.py directly. Finally, we lookup the database URL from the configuration.

Let's add our default configuration file. First, create a config package.

$ mkdir config
$ touch config/__init__.py

Then, create config/default.py and add the default database URL and debug flag.

# config/default.py
DATABASE = 'sqlite:///todos.db'
DEBUG = True

With these changes, we added the ability to override the database URL during testing by passing custom settings using an environment variable. We are now ready to create our first test.

Test setup

Remember, we wanted to use a custom database URL during testing. Let's create a testing configuration file in config/testing.py.

# config/testing.py
DATABASE = 'sqlite:///test.db'
TESTING = True

This overrides the default database URL to test.db. It also sets the testing flag, which enables better error reporting when using the test client.

Since our app is still small, we are going to simply include all our tests in tests.py and import the app after configuring the correct configuration file.

# tests.py
import os
import unittest
from os import path

base_path = path.dirname(path.realpath(__file__))
cfg_path = path.join(base_path, 'config', 'testing.py')
os.environ['TODO_SETTINGS'] = cfg_path

import server

We programmatically created the path to testing.py relative to the tests script and set the TODO_SETTINGS environment variable. Note that you pass the full path to the settings. This would allow creating a custom production configuration external from your app and simply providing the path when you start the server.

Also notice that the environment variable was set before import server. This ensures the correct settings are loading at import time.

We are finally ready to create our first test. We are going to write a test to ensure that the custom database URL is set when importing server.py.

class TodoTestCase(unittest.TestCase):

    def test_config_settings(self):
        config = server.app.config
        assert config['DATABASE'] == 'sqlite:///test.db'
        assert config['TESTING']
        assert config['DEBUG']


if __name__ == '__main__':
    unittest.main()

This is mostly boilerplate code using the unittest module. We create a test case by and run main if loaded as a script. This will run any method on our test case that begins with "test".

In the test, we assert that the test database was configured correctly and the testing and debug flags are set. Note that the debug flag was inherited from the default settings since the testing configuration did not override it.

Conclusion

In this article, we setup boilerplate for unit testing our app. To do this, we had to allow custom configuration of the database URL to test against a temporary database. In the next article, we will put this to good use and create unit tests against the todo api.

For further information, please visit the Flask Configuration Handling documentation.

The code is available on GitHub with tag config or compared to previous article.