Python Language
py.test
サーチ…
py.testの設定
py.test
は、Pythonで利用可能ないくつかのサードパーティのテストライブラリの 1つです。それはpip
を使用してインストールすることができます
pip install pytest
テストするコード
projectroot/module/code.py
追加機能をテストしているとします。
# projectroot/module/code.py
def add(a, b):
return a + b
テストコード
projectroot/tests/test_code.py
テストファイルを作成します。このファイルは 、テストファイルとして認識されるには、 test_
始まる必要があります。
# projectroot/tests/test_code.py
from module import code
def test_add():
assert code.add(1, 2) == 3
テストの実行
projectroot
我々は単に実行py.test
:
# ensure we have the modules
$ touch tests/__init__.py
$ touch module/__init__.py
$ py.test
================================================== test session starts ===================================================
platform darwin -- Python 2.7.10, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: /projectroot, inifile:
collected 1 items
tests/test_code.py .
================================================ 1 passed in 0.01 seconds ================================================
失敗したテスト
失敗したテストは、何がうまくいかなかったかに関する有用な出力を提供します:
# projectroot/tests/test_code.py
from module import code
def test_add__failing():
assert code.add(10, 11) == 33
結果:
$ py.test
================================================== test session starts ===================================================
platform darwin -- Python 2.7.10, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: /projectroot, inifile:
collected 1 items
tests/test_code.py F
======================================================== FAILURES ========================================================
___________________________________________________ test_add__failing ____________________________________________________
def test_add__failing():
> assert code.add(10, 11) == 33
E assert 21 == 33
E + where 21 = <function add at 0x105d4d6e0>(10, 11)
E + where <function add at 0x105d4d6e0> = code.add
tests/test_code.py:5: AssertionError
================================================ 1 failed in 0.01 seconds ================================================
テスト器具への紹介
より複雑なテストでは、テストするコードを実行する前に、設定が必要なことがあります。これをテスト機能自体で行うことは可能ですが、大規模なテスト機能が多すぎるため、セットアップがどこで停止し、テストが始まるかを知ることが難しくなります。また、さまざまなテスト機能間で重複したセットアップコードを取得することもできます。
私たちのコードファイル:
# projectroot/module/stuff.py
class Stuff(object):
def prep(self):
self.foo = 1
self.bar = 2
私たちのテストファイル:
# projectroot/tests/test_stuff.py
import pytest
from module import stuff
def test_foo_updates():
my_stuff = stuff.Stuff()
my_stuff.prep()
assert 1 == my_stuff.foo
my_stuff.foo = 30000
assert my_stuff.foo == 30000
def test_bar_updates():
my_stuff = stuff.Stuff()
my_stuff.prep()
assert 2 == my_stuff.bar
my_stuff.bar = 42
assert 42 == my_stuff.bar
これらは非常に簡単な例ですが、 Stuff
オブジェクトにもっと多くの設定が必要な場合は扱いにくくなります。私たちのテストケースの間には重複したコードがあることがわかりますので、まずそれを別の関数にリファクタリングしましょう。
# projectroot/tests/test_stuff.py
import pytest
from module import stuff
def get_prepped_stuff():
my_stuff = stuff.Stuff()
my_stuff.prep()
return my_stuff
def test_foo_updates():
my_stuff = get_prepped_stuff()
assert 1 == my_stuff.foo
my_stuff.foo = 30000
assert my_stuff.foo == 30000
def test_bar_updates():
my_stuff = get_prepped_stuff()
assert 2 == my_stuff.bar
my_stuff.bar = 42
assert 42 == my_stuff.bar
これは良く見えますが、私たちはまだmy_stuff = get_prepped_stuff()
呼び出しでテスト関数をmy_stuff = get_prepped_stuff()
しています。
救助のpy.testの備品!
フィクスチャは、テストセットアップ機能のはるかに強力で柔軟なバージョンです。私たちがここで活用している以上に多くのことを行うことができますが、一度に1ステップずつ進めていきます。
まず、変更get_prepped_stuff
と呼ばれる治具にprepped_stuff
。フィクスチャが後でテスト関数でどのように使用されるのかが原因で、動詞ではなく名詞でフィクスチャの名前を付ける必要があります。 @pytest.fixture
は、この特定の関数を通常の関数ではなくフィクスチャーとして処理する必要があることを示します。
@pytest.fixture
def prepped_stuff():
my_stuff = stuff.Stuff()
my_stuff.prep()
return my_stuff
ここで、フィクスチャを使用するようにテスト関数を更新する必要があります。これは、フィクスチャ名と正確に一致するパラメータを定義に追加することによって行われます。 py.testが実行されると、テストを実行する前にフィクスチャを実行し、フィクスチャの戻り値をそのパラメータを介してテスト関数に渡します。 (フィクスチャは値を返す必要はなく、外部リソースを呼び出す、ファイルシステムにアレンジする、データベースに値を入れる、セットアップに必要なテストを行うなどの)他の設定を行うことができます。
def test_foo_updates(prepped_stuff):
my_stuff = prepped_stuff
assert 1 == my_stuff.foo
my_stuff.foo = 30000
assert my_stuff.foo == 30000
def test_bar_updates(prepped_stuff):
my_stuff = prepped_stuff
assert 2 == my_stuff.bar
my_stuff.bar = 42
assert 42 == my_stuff.bar
これでなぜ名詞に名前を付けたのか分かります。 my_stuff = prepped_stuff
行はかなり役に立たないので、代わりにprepped_stuff
直接使用しましょう。
def test_foo_updates(prepped_stuff):
assert 1 == prepped_stuff.foo
prepped_stuff.foo = 30000
assert prepped_stuff.foo == 30000
def test_bar_updates(prepped_stuff):
assert 2 == prepped_stuff.bar
prepped_stuff.bar = 42
assert 42 == prepped_stuff.bar
今は器具を使用しています!フィクスチャの範囲を変更することで(テスト・モジュールごと、またはテスト・ファンクションごとに1回だけ実行する)、他のフィクスチャを使用するフィクスチャを構築し、フィクスチャをパラメータ化することでそのフィクスチャを使用するテストは、フィクスチャに与えられた各パラメータに対して1回、複数回実行されます)、それらを呼び出すモジュールから値を読み取るフィクスチャ...前述のように、フィクスチャは通常のセットアップ機能よりも多くのパワーと柔軟性を備えています。
テスト終了後のクリーンアップ。
コードが大きくなり、Stuffオブジェクトが特別なクリーンアップを必要としているとしましょう。
# projectroot/module/stuff.py
class Stuff(object):
def prep(self):
self.foo = 1
self.bar = 2
def finish(self):
self.foo = 0
self.bar = 0
すべてのテスト関数の最後にクリーンアップを呼び出すためのコードを追加することができますが、フィクスチャはこれを行うためのより良い方法を提供します。フィクスチャに関数を追加してファイナライザとして登録すると、フィクスチャを使用したテストが完了した後にファイナライザ関数のコードが呼び出されます。フィクスチャのスコープが単一の機能(モジュールやセッションのような)より大きい場合、スコープ内のすべてのテストが完了した後でファイナライザが実行されるため、モジュールの実行が完了した後、またはテスト実行セッションの終了時。
@pytest.fixture
def prepped_stuff(request): # we need to pass in the request to use finalizers
my_stuff = stuff.Stuff()
my_stuff.prep()
def fin(): # finalizer function
# do all the cleanup here
my_stuff.finish()
request.addfinalizer(fin) # register fin() as a finalizer
# you can do more setup here if you really want to
return my_stuff
関数内でファイナライザ関数を使用することは、特に複雑なフィクスチャを使用している場合には、一目で分かりにくいことがあります。代わりに、 歩留まりフィクスチャを使用して、人間が読める実行フローで同じことを行うことができます。唯一の本当の違いは、代わりに使用することであるreturn
我々が使用するyield
のセットアップが完了し、コントロールが後にすべてのクリーンアップコードを追加し、テスト関数に行くべき治具の一部でyield
。また、それをyield_fixtureとしてyield_fixture
し、 yield_fixture
それをどのように扱うかを知っているようにします。
@pytest.yield_fixture
def prepped_stuff(): # it doesn't need request now!
# do setup
my_stuff = stuff.Stuff()
my_stuff.prep()
# setup is done, pass control to the test functions
yield my_stuff
# do cleanup
my_stuff.finish()
そしてそれは、テスト・フィクスチャーの紹介です。
詳細については、 公式のpy.test fixtureのドキュメントと公式のyield fixtureのドキュメントを参照してください。