py.test は、テスト関数にオブジェクトを注入し、テストの実行に関連付けてそのライフサイクルを細かく制御できます。さらに別のオブジェクトでテスト関数を複数回実行することもできます。
オブジェクトを注入するための基本的な仕組みは funcarg 機構 とも呼ばれます。ある引数に対して、その引数を受け取るテスト関数が呼ばれることで最終的にオブジェクトが注入されるからです。古典的な xUnit のやり方とは異なり funcargs は 依存性の注入 に密接に関連したものです。その根拠は、テストコードを実行するために必要なオブジェクトからテストコードを分離するのに役立つからです。
テスト関数へ渡される値を作成するために、テスト関数のコンテキストに対してフルアクセスをもったファクトリー関数が呼ばれます。そして、ファイナライザーを登録したり、ライフサイクルキャッシュヘルパーを実行します。ファクトリー関数は、同じテストクラスかテストモジュール、ディレクトリ毎の conftest.py ファイル、外部プラグインであろうと、そのいずれでも実装できます。これにより、テストの実行に必要なテストコードとオブジェクトを完全に分離できます。
テスト関数は、 パラメーターテスト で説明したようなケースなら複数回呼び出すこともあります。これは、例えば、別々のデータベースのバックエンド、または複数の数値の引数セットをテストしたいときや、テスト関数の同じセットを再利用したいといったときにとても便利です。
py.test には 組み込み関数の引数 が付属していて、そのサンプルを紹介する節に洗練された利用方法があります。
簡単な自己完結型のテストモジュールを見てみましょう:
# ./test_simplefactory.py の内容
def pytest_funcarg__myfuncarg(request):
return 42
def test_function(myfuncarg):
assert myfuncarg == 17
このテスト関数は myfuncarg という名前のオブジェクトへの注入を必要とします。この場合 py.test は、同じモジュール内の pytest_funcarg__myfuncarg というファクトリー関数を見つけて呼び出します。
次のようにテストが実行されます:
$ py.test test_simplefactory.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
collecting ... collected 1 items
test_simplefactory.py F
================================= FAILURES =================================
______________________________ test_function _______________________________
myfuncarg = 42
def test_function(myfuncarg):
> assert myfuncarg == 17
E assert 42 == 17
test_simplefactory.py:5: AssertionError
========================= 1 failed in 0.01 seconds =========================
これは実際にテスト関数が myfuncarg 引数の値が 42 で呼び出されて、そのアサートに失敗します。py.test がどういった手順でテスト関数を呼び出すかを説明します:
関数の引数に誤字があったり、利用できないものを使おうとしたら、利用できる関数の引数の一覧と共にエラーが表示されるので注意してください。
いつでも次のようにして:
py.test --fixtures test_simplefactory.py
利用できる関数の引数 (“リソース” とも見なせる) を調べられます。
funcarg ファクトリー関数は、特別なテスト関数呼び出しに関連付けられた request オブジェクトを受け取ります。request オブジェクトは funcarg ファクトリーへ渡されて、テスト設定とコンテキストへのアクセスを提供します:
A request for a fixture from a test or fixture function.
A request object gives access to the requesting test context and has an optional param attribute in case the fixture is parametrized indirectly.
add finalizer/teardown function to be called after the last test within the requesting test context finished execution.
(deprecated) Return a testing resource managed by setup & teardown calls. scope and extrakey determine when the teardown function will be called so that subsequent calls to setup would recreate the resource. With pytest-2.3 you often do not need cached_setup() as you can directly declare a scope on a fixture function and register a finalizer through request.addfinalizer().
パラメタ: |
|
---|
Apply a marker to a single test function invocation. This method is useful if you don’t want to have a keyword/marker on all function invocations.
パラメタ: | marker – a _pytest.mark.MarkDecorator object created by a call to py.test.mark.NAME(...). |
---|
Dynamically retrieve a named fixture function argument.
As of pytest-2.3, it is easier and usually better to access other fixture values by stating it as an input argument in the fixture function. If you only can decide about using another fixture at test setup time, you may use this function to retrieve it inside a fixture function body.
別の関数の引数の値を取って呼び出す新たなテスト関数を追加することで、同じテスト関数に対して複数回の実行をパラメーター化して実行できます。簡単な自己完結型のサンプルコードを見てみましょう:
同じテスト関数に対する複数回呼び出しに生成する pytest_generate_tests フックを使うテストモジュールを見てみましょう:
# test_example.py の内容
def pytest_generate_tests(metafunc):
if "numiter" in metafunc.fixturenames:
metafunc.parametrize("numiter", range(10))
def test_func(numiter):
assert numiter < 9
このサンプルコードを実行すると range(10) のリストの要素を1つずつ引数に渡す test_func を10回実行します:
$ py.test test_example.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
collecting ... collected 10 items
test_example.py .........F
================================= FAILURES =================================
_______________________________ test_func[9] _______________________________
numiter = 9
def test_func(numiter):
> assert numiter < 9
E assert 9 < 9
test_example.py:6: AssertionError
==================== 1 failed, 9 passed in 0.02 seconds ====================
分かりやすいように numiter の値が 9 のときのみテストが失敗します。 pytest_generate_tests(metafunc) フックは、実際にテストを実行するときとは違うフェーズの、テストコレクションで呼ばれることに注意してください。では、テストコレクションがどうなるかをちょっと見てみましょう:
$ py.test --collect-only test_example.py
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4
collecting ... collected 10 items
<Module 'test_example.py'>
<Function 'test_func[0]'>
<Function 'test_func[1]'>
<Function 'test_func[2]'>
<Function 'test_func[3]'>
<Function 'test_func[4]'>
<Function 'test_func[5]'>
<Function 'test_func[6]'>
<Function 'test_func[7]'>
<Function 'test_func[8]'>
<Function 'test_func[9]'>
============================= in 0.00 seconds =============================
テスト実行時に 7 の値が渡されるときだけ実行したい場合は次のようにして行います:
$ py.test -v -k 7 test_example.py # または -k test_func[7]
=========================== test session starts ============================
platform linux2 -- Python 2.7.1 -- pytest-2.2.4 -- /home/hpk/venv/0/bin/python
collecting ... collected 10 items
test_example.py:5: test_func[7] PASSED
======================= 9 tests deselected by '-k7' ========================
================== 1 passed, 9 deselected in 0.01 seconds ==================
さらにパラメーターテストのサンプル を見たくなりますね。
metafunc オブジェクトは pytest_generate_tests フックへ渡されます。これはテスト関数を検査したり、テスト設定またはテスト関数が定義されているクラスやモジュールで指定された値を取るテストを生成するのに役立ちます:
metafunc.fixturenames: テスト関数へ渡される引数セット
metafunc.function: 対象となる Python のテスト関数
metafunc.cls: テスト関数が定義されているところのクラスオブジェクト、または None
metafunc.module: テスト関数が定義されているところのモジュールオブジェクト
metafunc.config: コマンドラインオプションと汎用的な設定オブジェクト
Add new invocations to the underlying test function using the list of argvalues for the given argnames. Parametrization is performed during the collection phase. If you need to setup expensive resources see about setting indirect=True to do it rather at test setup time.
パラメタ: |
|
---|
(deprecated, use parametrize) Add a new call to the underlying test function during the collection phase of a test run. Note that request.addcall() is called during the test collection phase prior and independently to actual test execution. You should only use addcall() if you need to specify multiple arguments of a test function.
パラメタ: |
|
---|