Adding Tests¶
We will now add tests for the models and the views and a few functional
tests in the tests.py
. Tests ensure that an application works, and
that it continues to work after some changes are made in the future.
Testing the Models¶
We write a test class for the model class Page
and another test class
for the initialize_sql
function.
To do so, we’ll retain the tutorial.tests.ViewTests
class provided as a
result of the pyramid_routesalchemy
project generator. We’ll add two
test classes: one for the Page
model named PageModelTests
, one for the
initialize_sql
function named InitializeSqlTests
.
Testing the Views¶
We’ll modify our tests.py
file, adding tests for each view function we
added above. As a result, we’ll delete the ViewTests
test in the file,
and add four other test classes: ViewWikiTests
, ViewPageTests
,
AddPageTests
, and EditPageTests
. These test the view_wiki
,
view_page
, add_page
, and edit_page
views respectively.
Functional tests¶
We test the whole application, covering security aspects that are not
tested in the unit tests, like logging in, logging out, checking that
the viewer
user cannot add or edit pages, but the editor
user
can, and so on.
Viewing the results of all our edits to tests.py
¶
Once we’re done with the tests.py
module, it will look a lot like the
below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 | import unittest
from pyramid import testing
def _initTestingDB():
from tutorial.models import DBSession
from tutorial.models import Base
from sqlalchemy import create_engine
engine = create_engine('sqlite:///:memory:')
DBSession.configure(bind=engine)
Base.metadata.bind = engine
Base.metadata.create_all(engine)
return DBSession
def _registerRoutes(config):
config.add_route('view_page', '{pagename}')
config.add_route('edit_page', '{pagename}/edit_page')
config.add_route('add_page', 'add_page/{pagename}')
class PageModelTests(unittest.TestCase):
def setUp(self):
self.session = _initTestingDB()
def tearDown(self):
self.session.remove()
def _getTargetClass(self):
from tutorial.models import Page
return Page
def _makeOne(self, name='SomeName', data='some data'):
return self._getTargetClass()(name, data)
def test_constructor(self):
instance = self._makeOne()
self.assertEqual(instance.name, 'SomeName')
self.assertEqual(instance.data, 'some data')
class InitializeSqlTests(unittest.TestCase):
def setUp(self):
from tutorial.models import DBSession
DBSession.remove()
def tearDown(self):
from tutorial.models import DBSession
DBSession.remove()
def _callFUT(self, engine):
from tutorial.models import initialize_sql
return initialize_sql(engine)
def test_it(self):
from sqlalchemy import create_engine
engine = create_engine('sqlite:///:memory:')
self._callFUT(engine)
from tutorial.models import DBSession, Page
self.assertEqual(DBSession.query(Page).one().data,
'This is the front page')
class ViewWikiTests(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
def tearDown(self):
testing.tearDown()
def _callFUT(self, request):
from tutorial.views import view_wiki
return view_wiki(request)
def test_it(self):
_registerRoutes(self.config)
request = testing.DummyRequest()
response = self._callFUT(request)
self.assertEqual(response.location, 'http://example.com/FrontPage')
class ViewPageTests(unittest.TestCase):
def setUp(self):
self.session = _initTestingDB()
self.config = testing.setUp()
def tearDown(self):
self.session.remove()
testing.tearDown()
def _callFUT(self, request):
from tutorial.views import view_page
return view_page(request)
def test_it(self):
from tutorial.models import Page
request = testing.DummyRequest()
request.matchdict['pagename'] = 'IDoExist'
page = Page('IDoExist', 'Hello CruelWorld IDoExist')
self.session.add(page)
_registerRoutes(self.config)
info = self._callFUT(request)
self.assertEqual(info['page'], page)
self.assertEqual(
info['content'],
'<div class="document">\n'
'<p>Hello <a href="http://example.com/add_page/CruelWorld">'
'CruelWorld</a> '
'<a href="http://example.com/IDoExist">'
'IDoExist</a>'
'</p>\n</div>\n')
self.assertEqual(info['edit_url'],
'http://example.com/IDoExist/edit_page')
class AddPageTests(unittest.TestCase):
def setUp(self):
self.session = _initTestingDB()
self.config = testing.setUp()
def tearDown(self):
self.session.remove()
testing.tearDown()
def _callFUT(self, request):
from tutorial.views import add_page
return add_page(request)
def test_it_notsubmitted(self):
_registerRoutes(self.config)
request = testing.DummyRequest()
request.matchdict = {'pagename':'AnotherPage'}
info = self._callFUT(request)
self.assertEqual(info['page'].data,'')
self.assertEqual(info['save_url'],
'http://example.com/add_page/AnotherPage')
def test_it_submitted(self):
from tutorial.models import Page
_registerRoutes(self.config)
request = testing.DummyRequest({'form.submitted':True,
'body':'Hello yo!'})
request.matchdict = {'pagename':'AnotherPage'}
self._callFUT(request)
page = self.session.query(Page).filter_by(name='AnotherPage').one()
self.assertEqual(page.data, 'Hello yo!')
class EditPageTests(unittest.TestCase):
def setUp(self):
self.session = _initTestingDB()
self.config = testing.setUp()
def tearDown(self):
self.session.remove()
testing.tearDown()
def _callFUT(self, request):
from tutorial.views import edit_page
return edit_page(request)
def test_it_notsubmitted(self):
from tutorial.models import Page
_registerRoutes(self.config)
request = testing.DummyRequest()
request.matchdict = {'pagename':'abc'}
page = Page('abc', 'hello')
self.session.add(page)
info = self._callFUT(request)
self.assertEqual(info['page'], page)
self.assertEqual(info['save_url'],
'http://example.com/abc/edit_page')
def test_it_submitted(self):
from tutorial.models import Page
_registerRoutes(self.config)
request = testing.DummyRequest({'form.submitted':True,
'body':'Hello yo!'})
request.matchdict = {'pagename':'abc'}
page = Page('abc', 'hello')
self.session.add(page)
response = self._callFUT(request)
self.assertEqual(response.location, 'http://example.com/abc')
self.assertEqual(page.data, 'Hello yo!')
class FunctionalTests(unittest.TestCase):
viewer_login = '/login?login=viewer&password=viewer' \
'&came_from=FrontPage&form.submitted=Login'
viewer_wrong_login = '/login?login=viewer&password=incorrect' \
'&came_from=FrontPage&form.submitted=Login'
editor_login = '/login?login=editor&password=editor' \
'&came_from=FrontPage&form.submitted=Login'
def setUp(self):
from tutorial import main
settings = { 'sqlalchemy.url': 'sqlite:///:memory:'}
app = main({}, **settings)
from webtest import TestApp
self.testapp = TestApp(app)
def tearDown(self):
del self.testapp
from tutorial.models import DBSession
DBSession.remove()
def test_root(self):
res = self.testapp.get('/', status=302)
self.assertEqual(res.location, 'http://localhost/FrontPage')
def test_FrontPage(self):
res = self.testapp.get('/FrontPage', status=200)
self.assertTrue('FrontPage' in res.body)
def test_unexisting_page(self):
self.testapp.get('/SomePage', status=404)
def test_successful_log_in(self):
res = self.testapp.get(self.viewer_login, status=302)
self.assertEqual(res.location, 'http://localhost/FrontPage')
def test_failed_log_in(self):
res = self.testapp.get(self.viewer_wrong_login, status=200)
self.assertTrue('login' in res.body)
def test_logout_link_present_when_logged_in(self):
self.testapp.get(self.viewer_login, status=302)
res = self.testapp.get('/FrontPage', status=200)
self.assertTrue('Logout' in res.body)
def test_logout_link_not_present_after_logged_out(self):
self.testapp.get(self.viewer_login, status=302)
self.testapp.get('/FrontPage', status=200)
res = self.testapp.get('/logout', status=302)
self.assertTrue('Logout' not in res.body)
def test_anonymous_user_cannot_edit(self):
res = self.testapp.get('/FrontPage/edit_page', status=200)
self.assertTrue('Login' in res.body)
def test_anonymous_user_cannot_add(self):
res = self.testapp.get('/add_page/NewPage', status=200)
self.assertTrue('Login' in res.body)
def test_viewer_user_cannot_edit(self):
self.testapp.get(self.viewer_login, status=302)
res = self.testapp.get('/FrontPage/edit_page', status=200)
self.assertTrue('Login' in res.body)
def test_viewer_user_cannot_add(self):
self.testapp.get(self.viewer_login, status=302)
res = self.testapp.get('/add_page/NewPage', status=200)
self.assertTrue('Login' in res.body)
def test_editors_member_user_can_edit(self):
self.testapp.get(self.editor_login, status=302)
res = self.testapp.get('/FrontPage/edit_page', status=200)
self.assertTrue('Editing' in res.body)
def test_editors_member_user_can_add(self):
self.testapp.get(self.editor_login, status=302)
res = self.testapp.get('/add_page/NewPage', status=200)
self.assertTrue('Editing' in res.body)
def test_editors_member_user_can_view(self):
self.testapp.get(self.editor_login, status=302)
res = self.testapp.get('/FrontPage', status=200)
self.assertTrue('FrontPage' in res.body)
|
Running the Tests¶
We can run these tests by using setup.py test
in the same way we did in
Running the Tests. However, first we must edit our setup.py
to
include a dependency on WebTest, which we’ve used in our tests.py
.
Change the requires
list in setup.py
to include WebTest
.
1 2 3 4 5 6 7 8 9 10 | requires = [
'pyramid',
'SQLAlchemy',
'transaction',
'pyramid_tm',
'pyramid_debugtoolbar',
'zope.sqlalchemy',
'docutils',
'WebTest', # add this
]
|
After we’ve added a dependency on WebTest in setup.py
, we need to rerun
setup.py develop
to get WebTest installed into our virtualenv. Assuming
our shell’s current working directory is the “tutorial” distribution
directory:
On UNIX:
$ ../bin/python setup.py develop
On Windows:
c:\pyramidtut\tutorial> ..\Scripts\python setup.py develop
Once that command has completed successfully, we can run the tests themselves:
On UNIX:
$ ../bin/python setup.py test -q
On Windows:
c:\pyramidtut\tutorial> ..\Scripts\python setup.py test -q
The expected result looks something like:
......................
----------------------------------------------------------------------
Ran 22 tests in 2.700s
OK