(機械翻訳) ユニット、統合、機能テスト¶
*単体テスト*は、あなたのアプリケーションの"単位"をテストする行為であることは驚くべきことではありません。この文脈では、"単位"は、多くの場合、クラスインスタンスの関数またはメソッドです。このユニットは、「テスト中のユニット」とも呼ばれます。
単体テストの目標は、**「テスト中のユニット」の一部の置換のみをテストすることです。 Python関数を使って特定のコードパスの結果を検証することを目的とした単体テストを書く場合、関数本体自体に存在する*コードをテストすることだけが必要です。関数が複雑なアプリケーション"ドメインオブジェクト"(リソース、データベース接続、SMTPサーバなど)を表すパラメータを受け入れる場合、ユニットテスト中にこの関数に渡される引数は*である必要はなく* "実"実装オブジェクトであってはなりません。たとえば、特定の関数実装では、SMTPサーバーオブジェクトを表す引数を受け入れることができますが、電子メールの送信につながるシステムが正常に動作しているときにこのオブジェクトのメソッドを呼び出すことができますが、このコードパスの単体テスト電子メールが実際に送信されることをテストする必要はありません。それは引数がSMTPサーバオブジェクトの "実際の"実装である場合に電子メールを送信する引数として提供されたオブジェクトのメソッドを関数が呼び出すことを保証するだけです。
一方、*統合テスト*は、2つ以上の "ユニット"間の相互作用が明示的にテストされる異なるテスト形式です。統合テストでは、アプリケーションのコンポーネントが連携して動作することを確認します。あなたは電子メールが統合テストで実際に送られたかどうか確認するかもしれません。
*機能テスト*は、アプリケーションが"文字通り"で動作する統合テストの一種です。あなたはコードテストの終わりをテストするので、電子メールが機能テストで実際に送信されたことを確認する必要があります。
特定のコードベースに対してそれぞれのタイプのテストを記述することが、しばしばベストプラクティスと見なされます。単体テストは、しばしばより良い"カバレッジ"を得る機会を提供します:テスト対象のユニットに引数やその潜在的なコードパスの* all *を実行させる環境を与えることは通常可能です。これは通常、一連の統合テストや機能テストでは簡単ではありませんが、統合テストと機能テストは、アプリケーションが実稼働環境で実行されるときに期待されるように、 。
a:app: Pyramid`アプリケーションのユニットテストと統合テストのために推奨されるメカニズムは、Python:mod: unittest`モジュールです。このモジュールの名前は:mod: unittest`ですが、実際にユニットテストと統合テストの両方を実行することができます。良い:mod: `unittest`チュートリアルは、 Dive Into Python <http://www.diveintopython.net/unit_testing/index.html> `_マーク・ピルグリム。
:app: `Pyramid`はユニット、統合、機能テストを書くのを容易にする多くの機能を提供します。この機能は、コードが:app: `Pyramid`関連のフレームワーク関数を呼び出すときに特に便利になります。
テストのセットアップとティアダウン¶
:app: Pyramid`は現在の:term: request`と現在の:term: アプリケーションレジストリ 'の2つの項目を保持するためにグローバル構造体(実際にはterm: thread local`)を使います。これらのデータ構造は、それぞれ:func: pyramid.threadlocal.get_current_request`と:func: pyramid.threadlocal.get_current_registry`関数を介して利用できます。これらの関数とそれらが返すデータ構造については、ref: `threadlocals_chapter`を参照してください。
あなたのコードがこれらの `` get_current_ * ``関数を使用するか、 `` get_current_ * ``関数を使用するapp: Pyramid`コードを呼び出す場合は、テスト設定でfunc: pyramid.testing.setUp`を呼び出す必要がありますあなたのテストティアダウンで:func: pyramid.testing.tearDown`を呼び出す必要があります。 :func: `〜pyramid.testing.setUp`はレジストリを:term: thread local`スタックにプッシュします。これは `` get_current_ * ``関数を動作させます。テスト中のコードで必要とされる余分な設定を行うために使用できる:term: `Configurator`オブジェクトを返します。 :func: `〜pyramid.testing.tearDown`は、スレッドローカルスタックをポップします。
通常、ConfiguratorをPyramidアプリケーションの `` main``ブロックと直接使用すると、 `` .commit``メソッドが呼び出されるまで(通常は:meth: `によって暗黙的にpyramid.config.Configurator.make_wsgi_app`メソッド)。 :func: `〜pyramid.testing.setUp`は* autocommitting * Configuratorですが、直ちに呼び出されるメソッドが暗示するすべてのアクションを実行します。これは、単体テストの目的のために、追加の設定文を追加した後に各テストで:meth: `pyramid.config.Configurator.commit`を呼び出すよりも便利です。
:func: 〜pyramid.testing.setUp`関数と:func:〜pyramid.testing.tearDown`関数を使用すると、テストケース内の各ユニットテストメソッドを独立したレジストリを持つ環境に提供することができます。単一のテスト期間中の独立したリクエストこの機能の使用例を次に示します。
1 2 3 4 5 6 7 8 9 | import unittest
from pyramid import testing
class MyTest(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
def tearDown(self):
testing.tearDown()
|
上記のことは: `` myTest``のテストケースメソッド内で呼び出された:func: 〜pyramid.threadlocal.get_current_registry`が、 config``のコンフィグレータインスタンスに関連する:term:`アプリケーションレジストリ `を返すことを確認します。 ` MyTest``に添付された各テストケースメソッドは、独立したレジストリを使用します。
:func: 〜pyramid.testing.setUp`と:func:〜pyramid.testing.tearDown`関数は、テストの環境に影響を与えるさまざまな引数を受け取ります。これらの関数でサポートされている余分な引数については、:ref: testing_module APIを参照してください。
:func: 〜pyramid.threadlocal.get_current_request`を1回のテストの途中で None``以外のものに戻したい場合、:term: request`オブジェクトを:func:あなたのテストの `` setUp``メソッドの中で `` pyramid.testing.setUp`:
1 2 3 4 5 6 7 8 9 10 | import unittest
from pyramid import testing
class MyTest(unittest.TestCase):
def setUp(self):
request = testing.DummyRequest()
self.config = testing.setUp(request=request)
def tearDown(self):
testing.tearDown()
|
:term: request`オブジェクトをテストケースの setUp``内で:func: pyramid.testing.setUp`に渡すと、直接的または間接的に呼び出す `` MyTest``テストケースに添付されたテストメソッド:func: 〜pyramid.threadlocal.get_current_request`はリクエストオブジェクトを受け取ります。さもなければ、テストの間、:func: `〜pyramid.threadlocal.get_current_request`は None``を返します。私たちは:class: `pyramid.testing.DummyRequest`によって提供される"ダミー "リクエスト実装を使用します。なぜなら、" real ":app: Pyramid`リクエストオブジェクトより構築が簡単だからです。
コンテキストマネージャを使用したテストのセットアップ¶
テスト設定を設定するもう一つの方法は、 `` with``文と:func: pyramid.testing.testConfig`を使って:term: context manager`を作成することです。コンテキストマネージャは、テスト対象コードの前に:func: pyramid.testing.setUp`を呼び出し、後で:func: pyramid.testing.tearDown`を呼び出します。
このスタイルは、自己完結型の小さなテストに役立ちます。例えば:
1 2 3 4 5 6 7 8 9 | import unittest
class MyTest(unittest.TestCase):
def test_my_function(self):
from pyramid import testing
with testing.testConfig() as config:
config.add_route('bar', '/bar/{id}')
my_function_which_needs_route_bar()
|
何?¶
スレッドローカルデータ構造は、特にフレームワークで使用されている場合は、常に混乱します。ごめんなさい。だから、経験則があります::func: 〜pyramid.threadlocal.get_current_registry`または:func:〜pyramid.threadlocal.get_current_request`関数を使うコードを呼び出しているかどうかを知りません。あなたはこれについて何も気にしませんが、テストコードを書こうと思っています。テストの `` setUp``メソッドと:func: pyramid.testingでfunc: pyramid.testing.setUp`を必ず呼び出してください。あなたのテストの `` tearDown``メソッドで tearDown`を実行します。テストしているアプリケーションが ` get_current * ``関数を呼び出さなければ、これは本当に何かを傷つけることはありません。
ユニットテストでの `` Configurator``と `` pyramid.testing`` APIの使用¶
`` Configurator`` APIと:mod: `pyramid.testing`モジュールは、ユニットテスト時に使用できるいくつかの関数を提供します。これらの関数は:term: `構成宣言`をcurrent:term: `アプリケーションレジストリ 'に呼び出しますが、通常、"実在する"機能の代わりに"スタブ"または"ダミー"コードが正常に実行されていれば、コードが呼び出されます。
たとえば、app: `Pyramid`ビュー関数をユニットテストするとします。
1 2 3 4 5 6 | from pyramid.httpexceptions import HTTPForbidden
def view_fn(request):
if request.has_permission('edit'):
raise HTTPForbidden
return {'greeting':'hello'}
|
注釈
このコードは、関連する:class: `pyramid.config.Configurator`インスタンスでレンダラーを必須指定として定義したことを意味します。それ以外の場合は、正常に実行されたときに失敗します。
ユニットテスト中に何も特別なことをしなければ、このビュー関数の:meth: 〜pyramid.request.Request.has_permission`の呼び出しは常に True``値を返します。 a:app: `Pyramid`アプリケーションが正常に起動すると、a:term: Configurator`に対して:term: 構成宣言`呼び出しを使用して:term: `アプリケーションレジストリ 'を生成します。しかし、このアプリケーションレジストリが作成され、実装されていない(例えば、コンフィグレータを認証ポリシーで初期化するなど)場合、ユニットテストでアプリケーションコードを呼び出すときのように、:app: `Pyramid API関数は失敗するか、結果。ですから、このビュー関数内でコードの枝をテストするにはどうすればよいでしょう:exc: 〜pyramid.httpexceptions.HTTPForbidden?
:app: Pyramid`によって提供されるテストAPIは、 main``関数が暗示する実際のアプリケーション設定を呼び出す必要なしに、ユニットテストフレームワークで使用するためのさまざまなアプリケーションレジストリ登録をシミュレートすることを可能にします。たとえば、上記の ` view_fn``( `` my.package``という名前のパッケージにあると仮定して)をテストしたい場合、テストAPIを使った:class: `unittest.TestCase`を書くことができます。
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 | import unittest
from pyramid import testing
class MyTest(unittest.TestCase):
def setUp(self):
self.config = testing.setUp()
def tearDown(self):
testing.tearDown()
def test_view_fn_forbidden(self):
from pyramid.httpexceptions import HTTPForbidden
from my.package import view_fn
self.config.testing_securitypolicy(userid='hank',
permissive=False)
request = testing.DummyRequest()
request.context = testing.DummyResource()
self.assertRaises(HTTPForbidden, view_fn, request)
def test_view_fn_allowed(self):
from my.package import view_fn
self.config.testing_securitypolicy(userid='hank',
permissive=True)
request = testing.DummyRequest()
request.context = testing.DummyResource()
response = view_fn(request)
self.assertEqual(response, {'greeting':'hello'})
|
上記の例では、class: unittest.TestCase`を継承した MyTest``テストケースを作成します。 :app: `Pyramid`アプリケーションにある場合、 `py.test``が実行されたときに見つかるでしょう。それは2つのテスト方法を持っています。
最初のテスト方法である `` test_view_fn_forbidden``は、認証ポリシーが現在のユーザに対して `` edit``パーミッションを禁止するときに `` view_fn``をテストします。その3行目は:meth: `〜pyramid.config.Configurator.testing_securitypolicy`メソッドを使用してダミー"非許可"認可ポリシーを登録します。これはユニットテストの特別なヘルパーメソッドです。
次に、WebObリクエストオブジェクトAPIをシミュレートする:class: pyramid.testing.DummyRequest`オブジェクトを作成します。 A:class: `pyramid.testing.DummyRequest`は、 real :app: Pyramid`リクエストより少ない設定しか必要としないリクエストオブジェクトです。製造された要求でテストされる関数を呼び出します。関数が呼び出されると、:meth: pyramid.request.Request.has_permission`は、アクセスを拒否するmeth:〜pyramid.config.Configurator.testing_securitypolicy`によって登録したダミー。 view関数が:exc: `〜pyramid.httpexceptions.HTTPForbidden`エラーを発生させることを確認します。
`` test_view_fn_allowed``という名前の第2のテストメソッドは、認証ポリシーがアクセスを許可する代替ケースをテストします。この結果を得るために:meth: `〜pyramid.config.Configurator.testing_securitypolicy`に異なる値を渡すことに注目してください。この最後に、view関数が値を返すと主張します。
このテストでは、 `` setUp``メソッドの:func: pyramid.testing.setUp`関数と tearDown``メソッドの:func: pyramid.testing.tearDown`関数を呼び出します。 unittestクラスで:func: pyramid.testing.setUp`の結果を config``として割り当てます。これは:term: `Configurator`オブジェクトであり、コンフィグレータのすべてのメソッドはテストの中で必要に応じて呼び出すことができます。テスト中に:class: `〜pyramid.config.Configurator APIのいずれかを使用する場合は、テストケースの` setUp``と `tearDown``で必ずこのパターンを使用してください。これらのメソッドは、テストごとに"fresh ":term: `application registry`を使用していることを確認します。
app: Pyramid`固有のテストAPIについては、:ref: testing_module`の章を参照してください。この章では、セキュリティポリシーの登録、パスへのリソースの登録、イベントリスナーの登録、ビューとビューのアクセス権の登録、リクエストとリソースの "ダミー"実装を表すクラスについて説明します。
参考
接頭辞 testing_`で始まる:ref: configuration_module`には、:term: `Configurator`のさまざまなメソッドもあります。
統合テストの作成¶
In:app: Pyramid、* unitテスト*は、通常、テスト対象のコードに実行可能なコンテキストを与えるために、"模擬 "または"ダミー "実装に依存します。
"統合テスト"は別の種類のテストを意味します。 a:app: Pyramid`統合テストのコンテキストでは、テストロジックは、テスト中のコードの機能を実行し、他の:app: Pyramid`フレームワークとの統合を実行します。
a:app: Pyramid`アプリケーションの統合テストを作成することは、通常、テストのセットアップコード内で:meth: pyramid.config.Configurator.include`を介してアプリケーションの `` includeme``関数を呼び出すことを意味します。これにより、app: `Pyramid`環境全体が設定され、アプリケーションが実行されるときに何が起きるかがシミュレートされます。これは、テストに適切なコンテキストがあることを確認し、コードの統合をapp: `Pyramid`の他の部分と一緒にテストすることを重大なものにしています。
参考
参照:ref: including_configuration
:class: 〜pyramid.config.Configurator APIを使って正しい"模擬 "登録を設定する単体テストを書くことは、しばしば統合テストの作成よりも好まれます。単体テストはより速く実行されます(テストごとに少ないため)、通常は推論が簡単です。
機能テストの作成¶
機能テストはリテラルアプリケーションをテストします。
Pyramidでは、機能テストは通常、アプリケーションにHTTP(S)要求を呼び出すためのAPIを提供する:term: WebTest`パッケージを使用して記述されます。簡単なテストとカバレッジレポートを提供するために ` py.test``と `` pytest-cov``も好きです。
どんなtesting:term: package`を使用する場合でも、そのパッケージの setup.py``ファイルに tests_require``の依存関係を必ず追加してください。 ` myproject / setup.py``ファイルの `` requires``ブロックの直後に次のコードを挿入します: `` myproject / setup.py``ファイルに以下のコードを挿入します。 。
11 12 13 14 15 16 17 18 19 20 21 22 23 | requires = [
'plaster_pastedeploy',
'pyramid',
'pyramid_jinja2',
'pyramid_debugtoolbar',
'waitress',
]
tests_require = [
'WebTest >= 1.3.1', # py3 compat
'pytest',
'pytest-cov',
]
|
依存関係を変更することを忘れないでください。
42 43 44 45 46 | zip_safe=False,
extras_require={
'testing': tests_require,
},
install_requires=requires,
|
依存関係を変更するときはいつも正しい `` pip install -e``コマンドを実行してください。
$VENV/bin/pip install -e ".[testing]"
あなたの `` MyPackage``プロジェクトでは、あなたの:term: package`は myproject``という名前で、 views``モジュールを含みます。これは:term: view`関数 `` my_view``ルートURLが呼び出されたときにHTML本文を返します。
1 2 3 4 5 6 from pyramid.view import view_config @view_config(route_name='home', renderer='templates/mytemplate.jinja2') def my_view(request): return {'project': 'MyProject'}
次の機能テストの例は、上記の呼び出しを示しています:term: view:
1 2 3 4 5 6 7 8 9 10 class FunctionalTests(unittest.TestCase): def setUp(self): from myproject import main app = main({}) from webtest import TestApp self.testapp = TestApp(app) def test_root(self): res = self.testapp.get('/', status=200) self.assertTrue(b'Pyramid' in res.body)
このテストを実行すると、各テストメソッドは、 `` myproject .__ init__``モジュールの `` main``関数を使ってterm: WebTest`を使って" real :term: `WSGI`アプリケーションを生成します: WSGIアプリケーションをラップします。結果を ` self.testapp``に代入します。 `` test_root``という名前のテストでは、 `` TestApp``の `` GET``メソッドを使ってルートURLを呼び出します。最後に、返されたHTMLに `` Pyramid``という文字列が含まれていると主張します。
class: webtest.app.TestApp`インスタンスで利用できるメソッドの詳細については、:term: WebTest`のドキュメントを参照してください。