(機械翻訳)テストの追加¶
新しい `` tests``サブパッケージにモデルとビューのテストといくつかの機能テストを追加します。テストでは、アプリケーションが正常に動作していることを確認し、将来変更が加えられた場合でも動作し続けます。
ファイル tests.py
は alchemy
クッキーカッターの一部として生成されましたが、テストが tests
サブパッケージに置かれるのは一般的なことです。テストサブパッケージの各モジュールには、アプリケーションの対応するモジュールのテストが含まれている必要があります。テストモジュールに test_
という接頭辞が付いていることを除いて、それぞれの対応するモジュールのペアは同じ名前でなければなりません。
まず tests.py
を削除し、新しいテストと新しい空のファイル tests/__init__.py
を含む新しいディレクトリを作成します。
警告
Pythonモジュールをパッケージにリファクタリングして、キャッシュファイル( `` .pyc``ファイルや `` __pycache__``フォルダ)を確実に削除することは非常に重要です! Pythonは、古いコードを使用して、フォルダに移動する前にキャッシュファイルの優先順位を決定します。なぜあなたの変更がうまくいかないのだろうか?
ビューをテストする¶
新しい `` tests / test_views.py``ファイルを作成し、他のテストクラスのベースとして使われる `` BaseTest``クラスを追加します。次に、以前にアプリケーションに追加した各ビュー関数のテストを追加します。 `` ViewWikiTests``、 `` ViewPageTests``、 `` AddPageTests``、および `` EditPageTests``の4つのテストクラスを追加します。これらは `` view_wiki``、 `` view_page``、 `` add_page``、 `` edit_page``のビューをテストします。
機能テスト¶
単体テストでテストされていないセキュリティ面(ログイン、ログアウト、 basic
ユーザーは作成していないページを編集できないことを確認しますが、 editor
ユーザーはできる、など。
`` tests``サブパッケージに対するすべての編集の結果を表示します。¶
次のように表示されるように `` tutorial/tests/test_views.py`` を作成します:
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 | import unittest
import transaction
from pyramid import testing
def dummy_request(dbsession):
return testing.DummyRequest(dbsession=dbsession)
class BaseTest(unittest.TestCase):
def setUp(self):
from ..models import get_tm_session
self.config = testing.setUp(settings={
'sqlalchemy.url': 'sqlite:///:memory:'
})
self.config.include('..models')
self.config.include('..routes')
session_factory = self.config.registry['dbsession_factory']
self.session = get_tm_session(session_factory, transaction.manager)
self.init_database()
def init_database(self):
from ..models.meta import Base
session_factory = self.config.registry['dbsession_factory']
engine = session_factory.kw['bind']
Base.metadata.create_all(engine)
def tearDown(self):
testing.tearDown()
transaction.abort()
def makeUser(self, name, role, password='dummy'):
from ..models import User
user = User(name=name, role=role)
user.set_password(password)
return user
def makePage(self, name, data, creator):
from ..models import Page
return Page(name=name, data=data, creator=creator)
class ViewWikiTests(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
self.config.include('..routes')
def tearDown(self):
testing.tearDown()
def _callFUT(self, request):
from tutorial.views.default import view_wiki
return view_wiki(request)
def test_it(self):
request = testing.DummyRequest()
response = self._callFUT(request)
self.assertEqual(response.location, 'http://example.com/FrontPage')
class ViewPageTests(BaseTest):
def _callFUT(self, request):
from tutorial.views.default import view_page
return view_page(request)
def test_it(self):
from ..routes import PageResource
# add a page to the db
user = self.makeUser('foo', 'editor')
page = self.makePage('IDoExist', 'Hello CruelWorld IDoExist', user)
self.session.add_all([page, user])
# create a request asking for the page we've created
request = dummy_request(self.session)
request.context = PageResource(page)
# call the view we're testing and check its behavior
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(BaseTest):
def _callFUT(self, request):
from tutorial.views.default import add_page
return add_page(request)
def test_it_pageexists(self):
from ..models import Page
from ..routes import NewPage
request = testing.DummyRequest({'form.submitted': True,
'body': 'Hello yo!'},
dbsession=self.session)
request.user = self.makeUser('foo', 'editor')
request.context = NewPage('AnotherPage')
self._callFUT(request)
pagecount = self.session.query(Page).filter_by(name='AnotherPage').count()
self.assertGreater(pagecount, 0)
def test_it_notsubmitted(self):
from ..routes import NewPage
request = dummy_request(self.session)
request.user = self.makeUser('foo', 'editor')
request.context = NewPage('AnotherPage')
info = self._callFUT(request)
self.assertEqual(info['pagedata'], '')
self.assertEqual(info['save_url'],
'http://example.com/add_page/AnotherPage')
def test_it_submitted(self):
from ..models import Page
from ..routes import NewPage
request = testing.DummyRequest({'form.submitted': True,
'body': 'Hello yo!'},
dbsession=self.session)
request.user = self.makeUser('foo', 'editor')
request.context = NewPage('AnotherPage')
self._callFUT(request)
page = self.session.query(Page).filter_by(name='AnotherPage').one()
self.assertEqual(page.data, 'Hello yo!')
class EditPageTests(BaseTest):
def _callFUT(self, request):
from tutorial.views.default import edit_page
return edit_page(request)
def makeContext(self, page):
from ..routes import PageResource
return PageResource(page)
def test_it_notsubmitted(self):
user = self.makeUser('foo', 'editor')
page = self.makePage('abc', 'hello', user)
self.session.add_all([page, user])
request = dummy_request(self.session)
request.context = self.makeContext(page)
info = self._callFUT(request)
self.assertEqual(info['pagename'], 'abc')
self.assertEqual(info['save_url'],
'http://example.com/abc/edit_page')
def test_it_submitted(self):
user = self.makeUser('foo', 'editor')
page = self.makePage('abc', 'hello', user)
self.session.add_all([page, user])
request = testing.DummyRequest({'form.submitted': True,
'body': 'Hello yo!'},
dbsession=self.session)
request.context = self.makeContext(page)
response = self._callFUT(request)
self.assertEqual(response.location, 'http://example.com/abc')
self.assertEqual(page.data, 'Hello yo!')
|
次のように表示されるように `` tutorial / tests / test_functional.py``を作成します:
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 | import transaction
import unittest
import webtest
class FunctionalTests(unittest.TestCase):
basic_login = (
'/login?login=basic&password=basic'
'&next=FrontPage&form.submitted=Login')
basic_wrong_login = (
'/login?login=basic&password=incorrect'
'&next=FrontPage&form.submitted=Login')
basic_login_no_next = (
'/login?login=basic&password=basic'
'&form.submitted=Login')
editor_login = (
'/login?login=editor&password=editor'
'&next=FrontPage&form.submitted=Login')
@classmethod
def setUpClass(cls):
from tutorial.models.meta import Base
from tutorial.models import (
User,
Page,
get_tm_session,
)
from tutorial import main
settings = {
'sqlalchemy.url': 'sqlite://',
'auth.secret': 'seekrit',
}
app = main({}, **settings)
cls.testapp = webtest.TestApp(app)
session_factory = app.registry['dbsession_factory']
cls.engine = session_factory.kw['bind']
Base.metadata.create_all(bind=cls.engine)
with transaction.manager:
dbsession = get_tm_session(session_factory, transaction.manager)
editor = User(name='editor', role='editor')
editor.set_password('editor')
basic = User(name='basic', role='basic')
basic.set_password('basic')
page1 = Page(name='FrontPage', data='This is the front page')
page1.creator = editor
page2 = Page(name='BackPage', data='This is the back page')
page2.creator = basic
dbsession.add_all([basic, editor, page1, page2])
@classmethod
def tearDownClass(cls):
from tutorial.models.meta import Base
Base.metadata.drop_all(bind=cls.engine)
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(b'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.basic_login, status=302)
self.assertEqual(res.location, 'http://localhost/FrontPage')
def test_successful_log_in_no_next(self):
res = self.testapp.get(self.basic_login_no_next, status=302)
self.assertEqual(res.location, 'http://localhost/')
def test_failed_log_in(self):
res = self.testapp.get(self.basic_wrong_login, status=200)
self.assertTrue(b'login' in res.body)
def test_logout_link_present_when_logged_in(self):
self.testapp.get(self.basic_login, status=302)
res = self.testapp.get('/FrontPage', status=200)
self.assertTrue(b'Logout' in res.body)
def test_logout_link_not_present_after_logged_out(self):
self.testapp.get(self.basic_login, status=302)
self.testapp.get('/FrontPage', status=200)
res = self.testapp.get('/logout', status=302)
self.assertTrue(b'Logout' not in res.body)
def test_anonymous_user_cannot_edit(self):
res = self.testapp.get('/FrontPage/edit_page', status=302).follow()
self.assertTrue(b'Login' in res.body)
def test_anonymous_user_cannot_add(self):
res = self.testapp.get('/add_page/NewPage', status=302).follow()
self.assertTrue(b'Login' in res.body)
def test_basic_user_cannot_edit_front(self):
self.testapp.get(self.basic_login, status=302)
res = self.testapp.get('/FrontPage/edit_page', status=302).follow()
self.assertTrue(b'Login' in res.body)
def test_basic_user_can_edit_back(self):
self.testapp.get(self.basic_login, status=302)
res = self.testapp.get('/BackPage/edit_page', status=200)
self.assertTrue(b'Editing' in res.body)
def test_basic_user_can_add(self):
self.testapp.get(self.basic_login, status=302)
res = self.testapp.get('/add_page/NewPage', status=200)
self.assertTrue(b'Editing' 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(b'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(b'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(b'FrontPage' in res.body)
def test_redirect_to_edit_for_existing_page(self):
self.testapp.get(self.editor_login, status=302)
res = self.testapp.get('/add_page/FrontPage', status=302)
self.assertTrue(b'FrontPage' in res.body)
|
次のように表示されるように `` tutorial / tests / test_initdb.py``を作成します:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | import os
import unittest
class TestInitializeDB(unittest.TestCase):
def test_usage(self):
from ..scripts.initializedb import main
with self.assertRaises(SystemExit):
main(argv=['foo'])
def test_run(self):
from ..scripts.initializedb import main
main(argv=['foo', 'development.ini'])
self.assertTrue(os.path.exists('tutorial.sqlite'))
os.remove('tutorial.sqlite')
|
次のように表示されるように `` tutorial / tests / test_security.py``を作成します:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | import unittest
from pyramid.testing import DummyRequest
class TestMyAuthenticationPolicy(unittest.TestCase):
def test_no_user(self):
request = DummyRequest()
request.user = None
from ..security import MyAuthenticationPolicy
policy = MyAuthenticationPolicy(None)
self.assertEqual(policy.authenticated_userid(request), None)
def test_authenticated_user(self):
from ..models import User
request = DummyRequest()
request.user = User()
request.user.id = 'foo'
from ..security import MyAuthenticationPolicy
policy = MyAuthenticationPolicy(None)
self.assertEqual(policy.authenticated_userid(request), 'foo')
|
次のように表示されるように `` tutorial / tests / test_user_model.py``を作成します:
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 | import unittest
import transaction
from pyramid import testing
class BaseTest(unittest.TestCase):
def setUp(self):
from ..models import get_tm_session
self.config = testing.setUp(settings={
'sqlalchemy.url': 'sqlite:///:memory:'
})
self.config.include('..models')
self.config.include('..routes')
session_factory = self.config.registry['dbsession_factory']
self.session = get_tm_session(session_factory, transaction.manager)
self.init_database()
def init_database(self):
from ..models.meta import Base
session_factory = self.config.registry['dbsession_factory']
engine = session_factory.kw['bind']
Base.metadata.create_all(engine)
def tearDown(self):
testing.tearDown()
transaction.abort()
def makeUser(self, name, role):
from ..models import User
return User(name=name, role=role)
class TestSetPassword(BaseTest):
def test_password_hash_saved(self):
user = self.makeUser(name='foo', role='bar')
self.assertFalse(user.password_hash)
user.set_password('secret')
self.assertTrue(user.password_hash)
class TestCheckPassword(BaseTest):
def test_password_hash_not_set(self):
user = self.makeUser(name='foo', role='bar')
self.assertFalse(user.password_hash)
self.assertFalse(user.check_password('secret'))
def test_correct_password(self):
user = self.makeUser(name='foo', role='bar')
user.set_password('secret')
self.assertTrue(user.password_hash)
self.assertTrue(user.check_password('secret'))
def test_incorrect_password(self):
user = self.makeUser(name='foo', role='bar')
user.set_password('secret')
self.assertTrue(user.password_hash)
self.assertFalse(user.check_password('incorrect'))
|
注釈
我々は優れた WebTest パッケージを利用してアプリケーションの機能テストを行っています。これは `` setup.py``の `` tests_require``セクションで定義されています。テスト目的だけに必要な他の依存関係はそこに追加することができ、 `` setup.py test``を実行すると自動的にインストールされます。
テストの実行¶
これらのテストは、テストを実行する と同じように実行できますが、最初にSQLiteデータベース` tutorial.sqlite`を削除します。データベースを削除しないと、テスト実行時に整合性エラーが表示されます。
UNIXの場合:
$ rm tutorial.sqlite
$ $VENV/bin/py.test -q
Windowsの場合:
c:\tutorial> del tutorial.sqlite
c:\tutorial> %VENV%\Scripts\py.test -q
予想される結果は、次のようになります。
................................
32 passed in 9.90 seconds