パむテスト

たえがき


歎史的な職業によるず、私はSQL-schikです。 しかし、運呜は私をBigDataに連れお行き、その埌、苊劎したした-Java、Python、および関数型プログラミングを習埗したしたScalaの孊習がリストに茉っおいたす。 実際、プロゞェクトの䞀郚で、Pythonコヌドをテストする必芁が生じたした。 QAのスタッフはこれらの目的のためにPyTestに助蚀したしたが、この獣がなぜ良いのかを明確に答えるこずは難しいず感じたした。 残念ながら、ロシア語のセグメントでは、この問題に関する情報はそれほど倚くありたせん。Yandexでどのように䜿甚されおいるか 、すべおが良い方法です。 同時に、この蚘事で説明されおいるものは、この道に沿っお旅を始める人にずっおはかなり耇雑に芋えたす。 公匏のドキュメントは蚀うたでもありたせんが、モゞュヌル自䜓を他の゜ヌスから芋぀けた埌に初めお意味がありたした。 私は、興味深いこずはそこに曞かれおいるずは蚀いたせんが、残念ながら、最初はたったくそうではありたせん。

Pythonナニットテスト


それが䜕であり、なぜ私が䌝えるべきポむントが芋えないのか-りィキペディアはただもっず知っおいたす。 Pythonの既存のモゞュヌルに関しおは、 Habréで詳しく説明されおいたす 。

必芁な知識の玹介


説明した時点で、Pythonの私の知識は非垞に衚面的でした-いく぀かの簡単なモゞュヌルを䜜成し、暙準的なものを知っおいたした。 しかし、PyTestずの衝突で、 ここずここ 、そしおyield構造䜓で知識ベヌスをデコレヌタヌで補充する必芁がありたした。

PyTestの長所ず短所


1APIからの独立定型なし。 同じunittestでコヌドがどのように芋えるか

コヌド
import unittest class TestUtilDate(unittest.TestCase): def setUp(self): #init_something() pass def tearDown(self): #teardown_something() pass def test_upper(self): self.assertEqual('foo'.upper(), 'FOO') def test_isupper(self): self.assertTrue('FOO'.isupper()) def test_failed_upper(self): self.assertEqual('foo'.upper(), 'FOo') if __name__ == '__main__': suite = unittest.TestLoader().loadTestsFromTestCase(TestUtilDate) unittest.TextTestRunner(verbosity=2).run(suite) 


PyTestでも同じこず

コヌド
 import pytest def setup_module(module): #init_something() pass def teardown_module(module): #teardown_something() pass def test_upper(): assert 'foo'.upper() == 'FOO' def test_isupper(): assert 'FOO'.isupper() def test_failed_upper(): assert 'foo'.upper() == 'FOo' 


2詳现レポヌト。 JUnitXMLぞのアップロヌドを含むJenkinsずの統合甚。 レポヌト自䜓のタむプは、远加のモゞュヌルによっお倉曎できたす色を含むそれらに぀いおは埌で別途説明したす。 たあ、䞀般的には、コン゜ヌルのカラヌレポヌトの方が䟿利です。赀のFAILEDはすぐに衚瀺されたす。

画像

3䟿利なアサヌトPythonの暙準。 異なるassert'ovの束党䜓を芚えおおく必芁はありたせん。

4すべおのレベルの動的なフィクスチャ。これは、自動的に特定のテストのために呌び出すこずができたす。

5フィクスチャの远加機胜戻り倀、ファむナラむザ、スコヌプ、リク゚ストオブゞェクト、自動䜿甚、ネストされたフィクスチャ

6テストのパラメヌタヌ化、぀たり、異なるパラメヌタヌセットを䜿甚した同じテストの起動。 䞀般的に、これはパラグラフ5「远加機胜フィクスチャ」を指したすが、別のパラグラフに倀するほど機䌚が倚いのです。

7マヌク。テストをスキップしたり、テストを倱敗ずしおマヌクしたりこれは開発䞭に䟿利な予想される動䜜です、テストスむヌトに名前を付けお名前でのみ実行できるようにしたす。

8プラグむン。 このモゞュヌルには、個別にむンストヌルできる远加モゞュヌルのかなり倧きなリストがありたす。

9unittestおよびnoseで蚘述されたテストを実行する機胜、぀たり、それらずの完党な䞋䜍互換性。

欠点に぀いおは、倚くはありたせんが、次のように蚀えたす。

1远加レベルのネストの欠劂テストのモゞュヌル、クラス、メ゜ッド、関数には、適切なレベルがありたす。 ただし、同じ関数が耇数のテストケヌスを持぀こずができる堎合たずえば、戻り倀ず゚ラヌのチェック、ロゞックには远加レベルのテストケヌスが必芁です。 これは、远加のモゞュヌルプラグむンpytest-describeによっお郚分的に補われたすが、適切なレベルのフィクスチャヌがないずいう問題が発生したすscope =“ describe”。 もちろん、これで生きるこずはできたすが、状況によっおは、PyTestの䞻な原則「単玔さず䟿利さのためのすべお」に違反する可胜性がありたす。

2生産を含む、モゞュヌルの個別のむンストヌルの必芁性。 それでも、unittestずdoctestは基本的なPythonツヌルキットの䞀郚であり、远加のゞェスチャヌは必芁ありたせん。

3PyTestを䜿甚するには、同じナニットテストよりもPythonの知識が少し必芁です「必芁な知識の抂芁」を参照。

カットの䞋のモゞュヌルずその機胜の詳现な説明。

テストを実行する


unittestの堎合、メむン関数呌び出しが䜿甚されたす。 したがっお、起動は「python unittest_example.py」ずいう圢匏になりたす。 同時に、テストスむヌトを実行するには、TestSuitでそれらを個別に組み合わせお実行する必芁がありたす。 PyTestは、フォルダ内のすべおのファむル再垰的にサブフォルダをトラバヌスするたたは指定されたファむルに぀いお、独自の名前test_ *クラス名のTest_ *ですべおのテストを収集したす。 ぀たり、呌び出し䟋は「py.test -v pytest_example.py」のようになりたす

基本的な備品


この堎合、フィクスチャは、テストに適切な環境を䜜成するために起動される関数ずメ゜ッドを呌び出したす。 unittestず同様に、PyTestにはすべおのレベルのフィクスチャの名前がありたす。

 import pytest def setup(): print ("basic setup into module") def teardown(): print ("basic teardown into module") def setup_module(module): print ("module setup") def teardown_module(module): print ("module teardown") def setup_function(function): print ("function setup") def teardown_function(function): print ("function teardown") def test_numbers_3_4(): print "test 3*4" assert 3*4 == 12 def test_strings_a_3(): print "test a*3" assert 'a'*3 == 'aaa' class TestUM: def setup(self): print ("basic setup into class") def teardown(self): print ("basic teardown into class") def setup_class(cls): print ("class setup") def teardown_class(cls): print ("class teardown") def setup_method(self, method): print ("method setup") def teardown_method(self, method): print ("method teardown") def test_numbers_5_6(self): print "test 5*6" assert 5*6 == 30 def test_strings_b_2(self): print "test b*2" assert 'b'*2 == 'bb' 

printコマンドによっお生成されたすべおの出力を衚瀺するには、-sフラグを指定しおテストを実行する必芁がありたす。

 tmp>py.test -s basic_fixtures.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 rootdir: tmp/, inifile: collected 4 items basic_fixtures.py module setup function setup basic setup into module test 3*4 .basic teardown into module function teardown function setup basic setup into module test a*3 .basic teardown into module function teardown class setup method setup basic setup into class test 5*6 .basic teardown into class method teardown method setup basic setup into class test b*2 .basic teardown into class method teardown class teardown module teardown ========================== 4 passed in 0.03 seconds 

この䟋は、各フィクスチャレベルの階局ず再珟性を完党に瀺しおいたすたずえば、setup_functionは各関数呌び出しの前に呌び出され、setup_moduleはモゞュヌル党䜓に察しお1回だけ呌び出されたす。 デフォルトのフィクスチャレベルが関数/メ゜ッドフィクスチャのセットアップず分解であるこずもわかりたす。

拡匵フィクスチャ


問題は、䞀郚のテストで特定の環境が必芁であるが、他のテストでは必芁ない堎合はどうすればよいかずいうこずです。 異なるモゞュヌルたたはクラスに広がりたすか それは非垞に快適で矎しく芋えたせん。 拡匵PyTestフィクスチャが圹立ちたす。

したがっお、PyTestで拡匵フィクスチャを䜜成するには、次のものが必芁です。

1pytestモゞュヌルをむンポヌトする
2@ pytest.fixtureデコレヌタを䜿甚しお、この関数がフィクスチャであるこずを瀺したす
3フィクスチャスコヌプのレベルを蚭定したす。 可胜な倀は、「関数」、「cls」、「モゞュヌル」、「セッション」です。 デフォルト倀=「関数」。
4このフィクスチャのティアダりンを呌び出す必芁がある堎合、ファむナラむザを远加する必芁がありたすフィクスチャに枡されたリク゚ストオブゞェクトのaddfinalizerメ゜ッドたたはyield構成䜓を䜿甚しお
5このフィクスチャの名前を関数パラメヌタヌのリストに远加したす

 import pytest @pytest.fixture() def resource_setup(request): print("resource_setup") def resource_teardown(): print("resource_teardown") request.addfinalizer(resource_teardown) def test_1_that_needs_resource(resource_setup): print("test_1_that_needs_resource") def test_2_that_does_not(): print("test_2_that_does_not") def test_3_that_does_again(resource_setup): print("test_3_that_does_again") 

以䞋を開始したす。

 tmp>py.test -s extended_fixture.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 rootdir: tmp/, inifile: collected 3 items extended_fixture.py resource_setup test_1_that_needs_resource .resource_teardown test_2_that_does_not .resource_setup test_3_that_does_again .resource_teardown ========================== 3 passed in 0.01 seconds 

高床なフィクスチャを呌び出す


高床なフィクスチャは、さらに2぀の方法で呌び出すこずができるこずに泚意しおください。

1デコレヌタ@ pytest.mark.usefixturesでテストを装食する
2フィクスチャに自動䜿甚フラグを䜿甚したす。 ただし、予期しないテスト動䜜が発生する可胜性があるため、この機胜は泚意しお䜿甚する必芁がありたす。
3テストパラメヌタを介した䞊蚘の方法

前の䟋を次に瀺したす。

 import pytest @pytest.fixture() def resource_setup(request): print("resource_setup") def resource_teardown(): print("resource_teardown") request.addfinalizer(resource_teardown) @pytest.fixture(scope="function", autouse=True) def another_resource_setup_with_autouse(request): print("another_resource_setup_with_autouse") def resource_teardown(): print("another_resource_teardown_with_autouse") request.addfinalizer(resource_teardown) def test_1_that_needs_resource(resource_setup): print("test_1_that_needs_resource") def test_2_that_does_not(): print("test_2_that_does_not") @pytest.mark.usefixtures("resource_setup") def test_3_that_does_again(): print("test_3_that_does_again") 

以䞋を開始したす。

 tmp>py.test -s call_extended_fixtures.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 rootdir: tmp/, inifile: collected 3 items call_extended_fixtures.py another_resource_setup_with_autouse resource_setup test_1_that_needs_resource .resource_teardown another_resource_teardown_with_autouse another_resource_setup_with_autouse test_2_that_does_not .another_resource_teardown_with_autouse another_resource_setup_with_autouse resource_setup test_3_that_does_again .resource_teardown another_resource_teardown_with_autouse ========================== 3 passed in 0.01 seconds 

分解拡匵噚具


䞊蚘のように、特定の拡匵フィクスチャに察しおティアダりンを呌び出す必芁がある堎合、2぀の方法で実装できたす。

1ファむナラむザをフィクスチャに远加したすフィクスチャに枡されたリク゚ストオブゞェクトのaddfinalizerメ゜ッドを介しお
2yieldコンストラクトを䜿甚しおPyTestバヌゞョン2.4以降

拡匵フィクスチャを䜜成する䟋で怜蚎した最初の方法。 次に、yieldを䜿甚しお同じ機胜を実蚌したす。 関数をフィクスチャずしお装食するずきにyieldを䜿甚するには、@ pytest.fixtureではなく@ pytest.yield_fixtureデコレヌタを䜿甚する必芁があるこずに泚意しおください。

 import pytest @pytest.yield_fixture() def resource_setup(): print("resource_setup") yield print("resource_teardown") def test_1_that_needs_resource(resource_setup): print("test_1_that_needs_resource") def test_2_that_does_not(): print("test_2_that_does_not") def test_3_that_does_again(resource_setup): print("test_3_that_does_again") 

ファむナラむザオプションず䞀臎するため、テキストに結論を远加したせん。

フィクスチャの戻り倀


オプションずしお、PyTestのフィクスチャはreturnを介しおテストに䜕かを返すこずができたす。 䜕らかの状態倀たたはオブゞェクトファむルなどになりたす。

 import pytest @pytest.fixture(scope="module") def resource_setup(request): print("\nconnect to db") db = {"Red":1,"Blue":2,"Green":3} def resource_teardown(): print("\ndisconnect") request.addfinalizer(resource_teardown) return db def test_db(resource_setup): for k in resource_setup.keys(): print "color {0} has id {1}".format(k, resource_setup[k]) def test_red(resource_setup): assert resource_setup["Red"] == 1 def test_blue(resource_setup): assert resource_setup["Blue"] != 1 

以䞋を開始したす。

 tmp>py.test -v -s return_value.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp/, inifile: collected 3 items return_value.py::test_db connect to db color Blue has id 2 color Green has id 3 color Red has id 1 PASSED return_value.py::test_red PASSED return_value.py::test_blue PASSED disconnect ========================== 3 passed in 0.02 seconds 

フィクスチャレベルスコヌプ


フィクスチャレベルは、「関数」、「cls」、「モゞュヌル」、「セッション」の倀を取りたす。 デフォルト倀=「関数」\

機胜-各テストのフィクスチャ実行
cls-各クラスのフィクスチャ実行
モゞュヌル-各モゞュヌルに察しおフィクスチャが起動されたす
セッション-フィクスチャはセッションごずに起動されたす぀たり、実際には1回

たずえば、前の䟋では、スコヌプを機胜に倉曎し、テストごずにデヌタベヌス接続を呌び出しお切断できたす。

 import pytest @pytest.fixture(scope="function") def resource_setup(request): print("\nconnect to db") db = {"Red":1,"Blue":2,"Green":3} def resource_teardown(): print("\ndisconnect") request.addfinalizer(resource_teardown) return db def test_db(resource_setup): for k in resource_setup.keys(): print "color {0} has id {1}".format(k, resource_setup[k]) def test_red(resource_setup): assert resource_setup["Red"] == 1 def test_blue(resource_setup): assert resource_setup["Blue"] != 1 

 tmp>py.test -v -s scope.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp/, inifile: collected 3 items scope.py::test_db connect to db color Blue has id 2 color Green has id 3 color Red has id 1 PASSED disconnect scope.py::test_red connect to db PASSED disconnect scope.py::test_blue connect to db PASSED disconnect ========================== 3 passed in 0.02 seconds 

フィクスチャは、PyTestによっお自動的にむンポヌトされるconftest.pyファむルでも説明できたす。 この堎合、フィクスチャには任意のレベルを蚭定できたすこのファむルの説明を介しおのみ、「セッション」レベルのフィクスチャを䜜成できたす。

たずえば、conftest.pyファむルず2぀のテストファむルで個別のセッションスコヌプフォルダヌを䜜成したすPyTestがモゞュヌルを自動的にむンポヌトするには、モゞュヌルの名前がtest_で始たる必芁がありたす。この動䜜は倉曎できたすが。

conftest.py

 import pytest @pytest.fixture(scope="session", autouse=True) def auto_session_resource(request): """ Auto session resource fixture """ print("auto_session_resource_setup") def auto_session_resource_teardown(): print("auto_session_resource_teardown") request.addfinalizer(auto_session_resource_teardown) @pytest.fixture(scope="session") def manually_session_resource(request): """ Manual set session resource fixture """ print("manually_session_resource_setup") def manually_session_resource_teardown(): print("manually_session_resource_teardown") request.addfinalizer(manually_session_resource_teardown) @pytest.fixture(scope="function") def function_resource(request): """ Function resource fixture """ print("function_resource_setup") def function_resource_teardown(): print("function_resource_teardown") request.addfinalizer(function_resource_teardown) 

test_session_scope1.py

 import pytest def test_1_that_does_not_need_session_resource(): print("test_1_that_does_not_need_session_resource") def test_2_that_does(manually_session_resource): print("test_2_that_does") 

test_session_scope2.py

 import pytest def test_3_that_uses_all_fixtures(manually_session_resource, function_resource): print("test_2_that_does_not") 

以䞋を開始したす。

 tmp\session scope>py.test -s -v ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\pro ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp\session scope, inifile: collected 3 items test_session_scope1.py::test_1_that_does_not_need_session_resource auto_session resource_setup test_1_that_does_not_need_session_resource PASSED test_session_scope1.py::test_2_that_does manually_session_resource_setup test_2_that_does PASSED test_session_scope2.py::test_3_that_uses_all_fixtures function_resource_setup test_2_that_does_not PASSEDfunction_resource_teardown manually_session_resource_teardown auto_session_resource_teardown ========================== 3 passed in 0.02 seconds 

たた、PyTestが--fixtures入力パラメヌタヌをサポヌトし、呌び出された堎合、conftest.pydocstringがあったで説明されおいるものを含む、䜿甚可胜なすべおのフィクスチャヌを返したす。

 tmp\session scope>py.test --fixtures ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 rootdir: tmp\session scope, inifile: collected 3 items cache Return a cache object that can persist state between testing sessions. 
..... path object. ----------------------- fixtures defined from conftest ------------------------ manually_session_resource Manual set session resource fixture function_resource Function resource fixture auto_session_resource Auto session resource fixture ============================== in 0.07 seconds 

リク゚ストオブゞェクト


拡匵フィクスチャを䜜成する䟋では、リク゚ストパラメヌタを枡したした。 これは、addfinalizerメ゜ッドを通じおファむナラむザヌを远加するために行われたした。 ただし、このオブゞェクトには倚くの属性ず他のメ゜ッド 公匏APIの完党なリストもありたす。

 import pytest @pytest.fixture(scope="function") def resource_setup(request): print request.fixturename print request.scope print request.function.__name__ print request.cls print request.module.__name__ print request.fspath def test_1(resource_setup): assert True class TestClass(): def test_2(self, resource_setup): assert True 

以䞋を開始したす。

 tmp>py.test -v -s request_object.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp/, inifile: collected 2 items request_object.py::test_1 resource_setup function test_1 None 08 tmp\request_object.py PASSED request_object.py::TestClass::test_2 resource_setup function test_2 08.TestClass 08 tmp\request_object.py PASSED ========================== 2 passed in 0.04 seconds 

パラメヌタ化


パラメヌタ化は、異なる䞀連の入力パラメヌタを䜿甚しお同じテストを実行する方法です。 たずえば、文字列が5文字より長い堎合は疑問笊を、5文字より短い堎合は感嘆笊を、文字列に正確に5文字がある堎合はドットを远加する関数がありたす。 したがっお、3぀のテストを䜜成する代わりに、1぀のテストを䜜成できたすが、異なるパラメヌタヌで呌び出したす。

テストのパラメヌタヌを蚭定するには、2぀の方法がありたす。

1倀の配列を枡すフィクスチャのparamsパラメヌタヌの倀を介しお。
぀たり、この堎合のフィクスチャは、パラメヌタヌを枡すラッパヌです。 そしお、それらは䞊蚘のリク゚ストオブゞェクトのparam属性を介しおテスト自䜓に枡されたす。
2デコレヌタラベル@ pytest.mark.parametrizeを介しお、倉数名のリストずその倀の配列が枡されたす。

だから最初の方法。

 import pytest def strange_string_func(str): if len(str) > 5: return str + "?" elif len(str) < 5: return str + "!" else: return str + "." @pytest.fixture(scope="function", params=[ ("abcdefg", "abcdefg?"), ("abc", "abc!"), ("abcde", "abcde.") ]) def param_test(request): return request.param def test_strange_string_func(param_test): (input, expected_output) = param_test result = strange_string_func(input) print "input: {0}, output: {1}, expected: {2}".format(input, result, expected_output) assert result == expected_output 

以䞋を開始したす。

 tmp>py.test -s -v parametrizing_base.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp/, inifile: collected 3 items parametrizing_base.py::test_strange_string_func[param_test0] input: abcdefg, output: abcdefg?, e xpected: abcdefg? PASSED parametrizing_base.py::test_strange_string_func[param_test1] input: abc, output: abc!, expected: abc! PASSED parametrizing_base.py::test_strange_string_func[param_test2] input: abcde, output: abcde., expec ted: abcde. PASSED ========================== 3 passed in 0.03 seconds 

1぀の䞍快な詳现を陀き、すべおが正垞に機胜したす。テストの名前によっお、どの皮類のパラメヌタヌがテストに枡されたかを理解するこずは䞍可胜です。 そしお、この堎合、フィクスチャパラメヌタIDが圹立ちたす。 テスト名のリスト長さはテストの数ず䞀臎する必芁がありたす、たたは最終名を生成する関数のいずれかを受け入れたす。

 import pytest def strange_string_func(str): if len(str) > 5: return str + "?" elif len(str) < 5: return str + "!" else: return str + "." @pytest.fixture(scope="function", params=[ ("abcdefg", "abcdefg?"), ("abc", "abc!"), ("abcde", "abcde.")], ids=["len>5","len<5","len==5"] ) def param_test(request): return request.param def test_strange_string_func(param_test): (input, expected_output) = param_test result = strange_string_func(input) print "input: {0}, output: {1}, expected: {2}".format(input, result, expected_output) assert result == expected_output def idfn(val): return "params: {0}".format(str(val)) @pytest.fixture(scope="function", params=[ ("abcdefg", "abcdefg?"), ("abc", "abc!"), ("abcde", "abcde.")], ids=idfn ) def param_test_idfn(request): return request.param def test_strange_string_func_with_ifdn(param_test_idfn): (input, expected_output) = param_test result = strange_string_func(input) print "input: {0}, output: {1}, expected: {2}".format(input, result, expected_output) assert result == expected_output 

以䞋を開始したす。

 tmp>py.test -s -v parametrizing_named.py --collect-only ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp/, inifile: collected 6 items <Module 'parametrizing_named.py'> <Function 'test_strange_string_func[len>5]'> <Function 'test_strange_string_func[len<5]'> <Function 'test_strange_string_func[len==5]'> <Function "test_strange_string_func_with_ifdn[params: ('abcdefg', 'abcdefg?')] "> <Function "test_strange_string_func_with_ifdn[params: ('abc', 'abc!')]"> <Function "test_strange_string_func_with_ifdn[params: ('abcde', 'abcde.')]"> ============================== in 0.03 seconds 

この堎合、远加パラメヌタヌ—collect-onlyを指定しおPyTestを起動したした。これにより、パラメヌタヌ化によっお生成されたすべおのテストを実行せずに収集できたす。

2番目の方法には1぀の利点がありたす異なるパラメヌタヌで耇数のラベルを指定するず、すべおの可胜なパラメヌタヌセット぀たり、パラメヌタヌのデカルト積でテストが開始されたす。

 import pytest @pytest.mark.parametrize("x", [1,2]) @pytest.mark.parametrize("y", [10,11]) def test_cross_params(x, y): print "x: {0}, y: {1}".format(x, y) assert True 

以䞋を開始したす。

 tmp>py.test -s -v parametrizing_combinations.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp/, inifile: collected 4 items parametrizing_combinations.py::test_cross_params[10-1] x: 1, y: 10 PASSED parametrizing_combinations.py::test_cross_params[10-2] x: 2, y: 10 PASSED parametrizing_combinations.py::test_cross_params[11-1] x: 1, y: 11 PASSED parametrizing_combinations.py::test_cross_params[11-2] x: 2, y: 11 PASSED ========================== 4 passed in 0.02 seconds 

パラメヌタヌ化ラベルでは、パラメヌタヌを蚭定する最初の方法ず同様に、出力にパラメヌタヌを衚瀺するidsパラメヌタヌを枡すこずもできたす。

 import pytest def idfn_x(val): return "x=({0})".format(str(val)) def idfn_y(val): return "y=({0})".format(str(val)) @pytest.mark.parametrize("x", [-1,2], ids=idfn_x) @pytest.mark.parametrize("y", [-10,11], ids=idfn_y) def test_cross_params(x, y): print "x: {0}, y: {1}".format(x, y) assert True @pytest.mark.parametrize("x", [-1,2], ids=["negative x","positive y"]) @pytest.mark.parametrize("y", [-10,11], ids=["negative y","positive y"]) def test_cross_params_2(x, y): print "x: {0}, y: {1}".format(x, y) assert True 

以䞋を開始したす。

 tmp>py.test -s -v parametrizing_combinations_named.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp/, inifile: collected 8 items parametrizing_combinations_named.py::test_cross_params[y=(-10)-x=(-1)] x: -1, y: -10 PASSED parametrizing_combinations_named.py::test_cross_params[y=(-10)-x=(2)] x: 2, y: -10 PASSED parametrizing_combinations_named.py::test_cross_params[y=(11)-x=(-1)] x: -1, y: 11 PASSED parametrizing_combinations_named.py::test_cross_params[y=(11)-x=(2)] x: 2, y: 11 PASSED parametrizing_combinations_named.py::test_cross_params_2[negative y-negative x] x: -1, y: -10 PASSED parametrizing_combinations_named.py::test_cross_params_2[negative y-positive y] x: 2, y: -10 PASSED parametrizing_combinations_named.py::test_cross_params_2[positive y-negative x] x: -1, y: 11 PASSED parametrizing_combinations_named.py::test_cross_params_2[positive y-positive y] x: 2, y: 11 PASSED ========================== 8 passed in 0.04 seconds 

耇数のフィクスチャずフィクスチャを䜿甚しおフィクスチャを呌び出したす


PyTestは、テストのために呌び出されるフィクスチャのリストを制限したせん。

 import pytest @pytest.fixture() def fixture1(request): print("fixture1") @pytest.fixture() def fixture2(request): print("fixture2") @pytest.fixture() def fixture3(request): print("fixture3") def test_1(fixture1, fixture2): print("test_1") def test_2(fixture1, fixture2, fixture3): print("test_2") 

 tmp>py.test -s -v multiply_fixtures.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp, inifile: collected 2 items multiply_fixtures.py::test_1 fixture1 fixture2 test_1 PASSED multiply_fixtures.py::test_2 fixture1 fixture2 fixture3 test_2 PASSED ========================== 2 passed in 0.01 seconds 

たた、どのフィクスチャヌも、それ自䜓に察しお任意の数のフィクスチャヌの実行を呌び出すこずができたす。

 import pytest @pytest.fixture() def fixture1(request, fixture2): print("fixture1") @pytest.fixture() def fixture2(request, fixture3): print("fixture2") @pytest.fixture() def fixture3(request): print("fixture3") def test_1(fixture1): print("test_1") def test_2(fixture2): print("test_2") 

 tmp>py.test -s -v fixtures_use_fixtures.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp, inifile: collected 2 items fixtures_use_fixtures.py::test_1 fixture3 fixture2 fixture1 test_1 PASSED fixtures_use_fixtures.py::test_2 fixture3 fixture2 test_2 PASSED ========================== 2 passed in 0.01 seconds 

タグ


PyTestは、「マヌク」ず呌ばれる@ pytest.markデコレヌタのクラスをサポヌトしおいたす。基本リストには、次のラベルが含た

れたす。1@ pytest.mark.parametrize-テストのパラメヌタヌ化䞊蚘
2@ pytest.mark.xfail-テストに合栌しないこずをマヌクし、PyTestはこれを期埅される動䜜ずしお認識したす開発された機胜のテストのタむムスタンプずしお有甚です。たた、このラベルは、テストがこのラベルでマヌクされる条件を受け入れる堎合がありたす。
3@ pytest.mark.skipif-テストをスキップするずきの条件を蚭定できたす;
4@ pytest.mark.usefixtures-テストのために呌び出されたすべおのフィクスチャをリストできたす。

䞀般的に、リストは広く、コマンド「py.test-マヌカヌ」。

 import pytest import sys @pytest.mark.xfail() def test_failed(): assert False @pytest.mark.xfail(sys.platform != "win64", reason="requires windows 64bit") def test_failed_for_not_win32_systems(): assert False @pytest.mark.skipif(sys.platform != "win64", reason="requires windows 64bit") def test_skipped_for_not_win64_systems(): assert False 

以䞋を開始したす。

 tmp>py.test -s -v basic_marks.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp, inifile: collected 3 items basic_marks.py::test_failed xfail basic_marks.py::test_failed_for_not_win32_systems xfail basic_marks.py::test_skipped_for_not_win64_systems SKIPPED ==================== 1 skipped, 2 xfailed in 0.02 seconds 

重芁な远加機胜は、ナヌザヌがラベルを任意に蚭定できるこずです。これにより、ラベル名で個別に実行するテストスむヌトを遞択し、-mスむッチで枡すこずができたす。

 import pytest def test_1(): print "test_1" @pytest.mark.critital_tests def test_2(): print "test_2" def test_3(): print "test_3" 

 tmp>py.test -s -v -m "critital_tests" custom_marks.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp, inifile: collected 3 items custom_marks.py::test_2 test_2 PASSED ================= 2 tests deselected by "-m 'critital_tests'" ================= =================== 1 passed, 2 deselected in 0.01 seconds ==================== 

ラベルは、pytest.iniモゞュヌルの説明を䜿甚しお説明し、すべおのモゞュヌルで䜿甚可胜にするこずができたす。同時に、「py.test --markers」で取埗した利甚可胜なタグのリストに衚瀺されたす。

pytest.ini

 # content of pytest.ini [pytest] markers = critical_test: mark test as critical. These tests must to be checked first. 


 tmp>py.test --markers @pytest.mark.critical_test: mark test as critical. These tests must to be checked first. 
...... 

たた、テストだけでなく、クラス、モゞュヌルむンポヌトされたモゞュヌルの属性を倉曎しお蚭定、たたはパラメヌタヌ化によっお取埗されたその起動にもラベルを付けるこずができたす次の䟋を参照。

 import pytest pytestmark = pytest.mark.level1 def test_1(): print "test_1" @pytest.mark.level2 class TestClass: def test_2(self): print "test_2" @pytest.mark.level3 def test_3(self): print "test_3" 

 tmp>py.test -s -v -m "level3" custom_marks_others.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp, inifile: pytest.ini collected 3 items custom_marks_others.py::TestClass::test_3 test_3 PASSED ===================== 2 tests deselected by "-m 'level3'" ===================== =================== 1 passed, 2 deselected in 0.07 seconds ==================== 

 tmp>py.test -s -v -m "level2" custom_marks_others.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp, inifile: pytest.ini collected 3 items custom_marks_others.py::TestClass::test_2 test_2 PASSED custom_marks_others.py::TestClass::test_3 test_3 PASSED ===================== 1 tests deselected by "-m 'level2'" ===================== =================== 2 passed, 1 deselected in 0.03 seconds ==================== 

 tmp>py.test -s -v -m "level1" custom_marks_others.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp, inifile: pytest.ini collected 3 items custom_marks_others.py::test_1 test_1 PASSED custom_marks_others.py::TestClass::test_2 test_2 PASSED custom_marks_others.py::TestClass::test_3 test_3 PASSED ========================== 3 passed in 0.02 seconds =========================== 

 import pytest @pytest.mark.parametrize(("x","expected"), [ (1,2), pytest.mark.critical((2,3)), (3,4) ]) def test_inc(x,expected): print x, "+ 1 = ", expected assert x + 1 == expected 

 tmp>py.test -s -v -m "critical" custom_marks_params.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp, inifile: pytest.ini collected 3 items custom_marks_params.py::test_inc[2-3] 2 + 1 = 3 PASSED ==================== 2 tests deselected by "-m 'critical'" ==================== =================== 1 passed, 2 deselected in 0.02 seconds ==================== 

䟋倖凊理


もちろん、テスト甚の本栌的なモゞュヌルずしお、PyTestでは「with pytest.raises」を䜿甚しお、返された䟋倖の正確さを確認するこずもできたす。

 import pytest def f(): print 1/0 def test_exception(): with pytest.raises(ZeroDivisionError): f() 

 tmp>py.test -s -v check_exception.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp, inifile: pytest.ini collected 1 items check_exception.py::test_exception PASSED ========================== 1 passed in 0.01 seconds 

名前たたはIDによるテストの実行


module.py::class::method orの圢匏でモゞュヌルぞのフルパスをリストするこずにより、モゞュヌルから個々のテストを実行できたす。module.py::function。たた、名前の䞀郚を-kフラグで枡したす。

 import pytest def test_one(): print "test_one" def test_one_negative(): print "test_one_negative" def test_two(): print "test_one_negative" 

 tmp>py.test -s -v call_by_name_and_id.py::test_two ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp, inifile: pytest.ini collected 4 items call_by_name_and_id.py::test_two test_one_negative PASSED ========================== 1 passed in 0.04 seconds 

 tmp>py.test -s -v -k "one" call_by_name_and_id.py ============================= test session starts ============================= platform win32 -- Python 2.7.8, pytest-2.8.2, py-1.4.30, pluggy-0.3.1 -- c:\prog ram files (x86)\python27\python.exe cachedir: .cache rootdir: tmp, inifile: pytest.ini collected 3 items call_by_name_and_id.py::test_one test_one PASSED call_by_name_and_id.py::test_one_negative test_one_negative PASSED ======================== 1 tests deselected by '-kone' ======================== =================== 2 passed, 1 deselected in 0.01 seconds ==================== 

EclipseでのPyDevずの統合


PyTestは、EclipseのPyDevモゞュヌルのPyUnitコンポヌネントに統合されおいるこずに蚀及したいず思いたす。蚭定で、䜿甚する必芁があるこずを指定する必芁がありたす。

画像

远加モゞュヌル


PyTestには倚数のアドオンモゞュヌルがありたす。
興味のあるモゞュヌルずその理由のみに蚀及するこずができたすモゞュヌルの詳现に぀いおは、䞊蚘のリンクを参照しおください

pytest-describe-別のレベルの抜象化を远加したす同等のmodule-function-testcaseずしおmodule-description-function。

pytest-instafail-モゞュヌルの基本的な動䜜を倉曎しお、セッション党䜓の終わりではなく、実行䞭にすべおの゚ラヌずクラッシュが衚瀺されるようにしたす。

pytest-marks-テストに耇数のラベルを同時に蚭定できたす

 @pytest.mark.red @pytest.mark.green @pytest.mark.blue def some_test_method(self): ..... 

pytest-ordering -Runラベルでテストを実行する順序を手動で蚭定できたす。

 import pytest @pytest.mark.run(order=2) def test_foo(): assert True @pytest.mark.run(order=1) def test_bar(): assert True 

pytest-pep8 -pep-8芏玄に準拠しおいるかどうかテストコヌドを確認できたす。

pytest-smartcov-完党たたは郚分的なテストのコヌドのカバレッゞを確認できたす。

pytest-timeout-コマンドラむンパラメヌタヌたたは特別なラベルを䜿甚しお、タむムアりトでテストを完了できたす。

pytest-sugar -PyTestの出力の倖芳を倉曎し、進行状況バヌず完了率を远加できたす。それは堎所にかかわらず、矎しく芋え、あたり有益ではありたせん。

あずがき


PyTestの基本的なドキュメントには、その拡匵された䜿甚に関する倚くの興味深い䟋が蚘茉されおいたす。しかし、次回はそれらに぀いおお話したいず思いたす基本的なPyTestの動䜜の管理テストの収集に䜿甚されるテンプレヌト、コマンドラむンの高床なオプションの远加、パラメヌタヌ化䞭のテストの収集プロセスの制埡metafuncオブゞェクトなど。

Source: https://habr.com/ru/post/J269759/


All Articles