Merge pull request #50 from ryanpetrello/next

Work on the scaffolded "Quickstart" project
This commit is contained in:
Ryan Petrello
2012-03-08 19:21:34 -08:00
30 changed files with 107 additions and 245 deletions

View File

@@ -82,11 +82,6 @@ code here to define tables, ORM definitions, and parse bindings from your
configuration file.
.. note::
The base project contains some ready-to-run tests. Try running
``py.test`` (the recommended test runner for Pecan) and watch them pass!
.. _running_application:
Running the application

View File

@@ -2,108 +2,4 @@
Unit Testing
=============
UnitTesting in Pecan is handled by ``WebTest``. It creates a fake Pecan
application that in turn allows you to make assertions on how those requests
and responses are being handled without starting an HTTP server at all.
Tools
-----
Pecan recommends using ``py.test``. It is actually a project requirement when
you install Pecan so you should already have it installed.
Structure
---------
This guide assumes that you have all your tests in a ``tests`` directory. If
you have created a project from the ``base`` project template that Pecan
provides, you should already have this directory with a few tests.
The template project uses UnitTest-type tests and some of those tests use
WebTest. We will describe how they work in the next section.
This is how running those tests with ``py.test`` would look like::
$ py.test
============== test session starts =============
platform darwin -- Python 2.6.1 -- pytest-2.0.1
collected 11 items
./tests/test_config.py .........
./tests/test_root.py ..
========== 11 passed in 0.30 seconds ===========
Configuration and Testing
-------------------------
When you create a new project using the ``base`` project template, Pecan adds
a reference to its ``py.test`` plugin to your project's ``setup.cfg`` file.
This handles loading your Pecan configuration and setting up your app as
defined by your project's ``app.py`` file.
If you've created your own project without using Pecan's template, you can
load the plugin yourself by adding this to your ``setup.cfg`` file::
[pytest]
addopts = -p pecan.testing --with-config=./config.py
Alternatively, you can just pass those options to ``py.test`` directly.
By default, Pecan's testing plugin assumes you will be using the ``config.py``
configuration file to run your tests. To change which configuration file gets
used once, run ``py.test`` with the `--with-config` option. To make the change
permanent, modify that option in the `addopts` setting of your ``setup.cfg``
file.
Pecan's ``py.test`` plugin exposes two new variables in the ``py.test``
namespace: ``temp_dir`` and ``wsgi_app``.
``py.test.temp_dir`` is a temporary directory that you can use for your tests.
It's created at startup and deleted after all tests have completed. When using
locally distributed testing with py.test, this is guaranteed to be shared by
each test process. This is useful if you need to create some initial resource
(e.g., a database template) that is later copied by each test. If you're using
remotely distributed testing, the directory won't be shared across nodes.
``py.test.wsgi_app`` is your Pecan app loaded and configured per your project's
``app.py`` file. In your test's ``setUp`` method, you would wrap this with
``TestApp``::
from unittest import TestCase
from webtest import TestApp
import py.test
class TestRootController(TestCase):
def setUp(self):
self.app = TestApp(py.test.wsgi_app)
Using WebTest with a UnitTest
-----------------------------
Once you have a ``setUp`` method with your ``TestApp`` created, you have a
wealth of actions provided within the test class to interact with your Pecan
application::
* POST => self.app.post
* GET => self.app.get
* DELETE => self.app.delete
* PUT => self.app.put
For example, if you want to assert that you can get to the root of your
application, you could do something similar to this::
response = self.app.get('/')
assert response.status_int == 200
If you are expecting error responses from your application, make sure to pass
`expect_errors=True`::
response = self.app.get('/url/does/not/exist', expect_errors=True)
assert response.status_int == 404
If you would like to dig in to more examples in how to test and verify more
actions, take a look at the
`WebTest documentation <http://pythonpaste.org/webtest/>`_
TODO

View File

@@ -19,6 +19,7 @@ DEFAULT = {
'static_root' : 'public',
'template_path' : '',
'debug' : False,
'logging' : False,
'force_canonical' : True,
'errors' : {
'__force_dict__' : True

View File

@@ -1,11 +1,10 @@
from pecan import expose
from pecan import expose, redirect
from formencode import Schema, validators as v
from webob.exc import status_map
class SampleForm(Schema):
name = v.String(not_empty=True)
age = v.Int(not_empty=True)
class SearchForm(Schema):
q = v.String(not_empty=True)
class RootController(object):
@@ -19,19 +18,18 @@ class RootController(object):
@index.when(
method = 'POST',
template = 'success.html',
schema = SampleForm(),
schema = SearchForm(),
error_handler = '/index',
htmlfill = dict(auto_insert_errors = True, prefix_error = False)
htmlfill = dict(auto_insert_errors = True)
)
def index_post(self, name, age):
return dict(name=name)
def index_post(self, q):
redirect('http://pecan.readthedocs.org/en/latest/search.html?q=%s' % q)
@expose('error.html')
def error(self, status):
try:
status = int(status)
except ValueError:
status = 0
except ValueError: # pragma: no cover
status = 500
message = getattr(status_map.get(status), 'explanation', '')
return dict(status=status, message=message)

View File

@@ -20,23 +20,15 @@
</p>
<p>
To get an idea of how to develop applications with Pecan,
here is a simple form:
...or you can search the documentation here:
</p>
<form method="POST" action="/">
<table>
<tr>
<td><label for="name">Your Name:</label></td>
<td><input name="name" /></td>
</tr>
<tr>
<td><label for="age">Your Age:</label></td>
<td><input name="age" /></td>
</tr>
</table>
<input type="submit" value="Submit" />
<fieldset>
<input name="q" />
<input type="submit" value="Search" />
<fieldset>
<small>Enter search terms or a module, class or function name.</small>
</form>
</div>

View File

@@ -1,13 +0,0 @@
<%inherit file="layout.html"/>
## override the title
<%def name="title()">
Success!
</%def>
## now define the body
<header>
<h1><img src="/images/logo.png" /></h1>
</header>
<p>Your form submission was successful! Thanks, ${name}!</p>
<p><a href="/">Go Back</a></p>

View File

@@ -0,0 +1,22 @@
import os
from unittest import TestCase
from pecan.configuration import set_config
from pecan.testing import load_test_app
__all__ = ['FunctionalTest']
class FunctionalTest(TestCase):
"""
Used for functional tests where you need to test your
literal application and its integration with the framework.
"""
def setUp(self):
self.app = load_test_app(os.path.join(
os.path.dirname(__file__),
'config.py'
))
def tearDown(self):
set_config({}, overwrite=True)

View File

@@ -0,0 +1,27 @@
# Server Specific Configurations
server = {
'port' : '8080',
'host' : '0.0.0.0'
}
# Pecan Application Configurations
app = {
'root' : '${package}.controllers.root.RootController',
'modules' : ['${package}'],
'static_root' : '%(confdir)s/../../public',
'template_path' : '%(confdir)s/../templates',
'reload' : True,
'debug' : True,
'logging' : False,
'errors' : {
'404' : '/error/404',
'__force_dict__' : True
}
}
# Custom Configurations must be in Python dictionary format::
#
# foo = {'bar':'baz'}
#
# All configurations are accessible at::
# pecan.conf

View File

@@ -1,41 +0,0 @@
from unittest import TestCase
import config
class TestConfigServer(TestCase):
def test_server_port(self):
assert config.server['port'] == '8080'
def test_server_host(self):
assert config.server['host'] == '0.0.0.0'
class TestConfigApp(TestCase):
def test_app_root(self):
root = config.app['root']
assert root.__class__.__name__ == 'RootController'
def test_app_modules(self):
assert len(config.app['modules']) == 1
def test_app_static_root(self):
assert 'public' in config.app['static_root']
def test_app_template_path(self):
assert 'templates' in config.app['template_path']
def test_app_reload(self):
assert config.app['reload']
def test_app_debug(self):
assert config.app['debug']
def test_app_errors(self):
errors = {
'404' : '/error/404',
'__force_dict__' : True
}
assert config.app['errors'] == errors

View File

@@ -0,0 +1,19 @@
from unittest import TestCase
from webtest import TestApp
from ${package}.tests import FunctionalTest
class TestRootController(FunctionalTest):
def test_get(self):
response = self.app.get('/')
assert response.status_int == 200
def test_search(self):
response = self.app.post('/', params={'q' : 'RestController'})
assert response.status_int == 302
assert response.headers['Location'] == 'http://pecan.readthedocs.org/en/latest/search.html?q=RestController'
def test_get_not_found(self):
response = self.app.get('/a/bogus/url', expect_errors=True)
assert response.status_int == 404

View File

@@ -1,18 +0,0 @@
from unittest import TestCase
from webtest import TestApp
import py.test
class TestRootController(TestCase):
def setUp(self):
self.app = TestApp(py.test.wsgi_app)
def test_get(self):
response = self.app.get('/')
assert response.status_int == 200
def test_get_not_found(self):
response = self.app.get('/a/bogus/url', expect_errors=True)
assert response.status_int == 404

View File

@@ -0,0 +1,7 @@
from unittest import TestCase
class TestUnits(TestCase):
def test_units(self):
assert 5 * 5 == 25

View File

@@ -20,9 +20,13 @@ div#content {
}
form {
margin: 0 1em;
padding: 1em;
border: 5px transparent;
margin: 0;
padding: 0;
border: 0;
}
fieldset {
border: 0;
}
input.error {

View File

@@ -1,2 +0,0 @@
[pytest]
addopts = -p pecan.testing --with-config=./config.py

View File

@@ -0,0 +1,6 @@
[nosetests]
match=^test
where=${package}
nocapture=1
cover-package=${package}
cover-erase=1

View File

@@ -15,6 +15,7 @@ setup(
install_requires = [
"pecan",
],
test_suite = '${package}',
zip_safe = False,
paster_plugins = ${egg_plugins},
include_package_data = True,

View File

@@ -1,10 +0,0 @@
__all__ = ['collector']
def collector():
try:
from unittest import TestLoader
assert hasattr(TestLoader, 'discover')
return TestLoader().discover('pecan.tests')
except:
import unittest2
return unittest2.collector

View File

@@ -1,3 +0,0 @@
def setup_app(config):
assert config.foo.sample_key == True
return 'DEPLOYED!'

View File

@@ -1,9 +0,0 @@
import sample_app
app = {
'modules': ['sample_app']
}
foo = {
'sample_key': True
}

View File

@@ -1,9 +0,0 @@
import sample_app_missing
app = {
'modules': ['sample_app_missing']
}
foo = {
'sample_key': True
}

View File

@@ -65,8 +65,7 @@ setup(
scripts = ['bin/pecan'],
zip_safe = False,
install_requires = requirements,
tests_require = tests_require,
test_suite = test_suite,
test_suite = 'pecan',
entry_points = """
[paste.paster_command]
pecan-serve = pecan.commands:ServeCommand