- 追加された行はこの色です。
- 削除された行はこの色です。
#author("2018-10-08T09:26:22+00:00","","")
#author("2018-10-08T12:20:49+00:00","","")
* pytest入門 [#e9ffd5bd]
#setlinebreak(on);
#html(<style>div * { line-height:1.5;}</style>)
#contents
** インストール [#q4177cd9]
#html(<div style="padding-left:10px;">)
#myterm2(){{
pip install pytest
pip install pytest-cov
}}
#html(</div>)
** フォルダ構成 [#f0395e6c]
** ファイル/フォルダ構成 [#f0395e6c]
#html(<div style="padding-left:10px;">)
tests
├── __init__.py
└── sample
├── __init__.py
|── conftest.py # セットアップコードを記述
└── test_code.py # テストケースを記述
https://docs.pytest.org/en/latest/goodpractices.html#choosing-a-test-layout-import-rules
pytestのドキュメントにはいくつかの構成が記載されている。
規模や用途に応じて選択すれば良いと思うが、src と tests とフォルダ分けする場合のPATHの指定方法がいまいち良くわからない。
テストコード自体をどこから探すのかは、pytest.ini または pytest 実行時のオプションで testpaths を指定すれば良いらしいが、
テスト対象のソースの格納先をどのように指定するのか良くわからない。(現時点では深く追ってないので詳細不明)
#html(<div style="padding:0 40px 0 10px; background:#efefef;border: 1px solid #333;display:inline-block;vertical-align:top;">)
setup.py
mypkg/
...
tests/
__init__.py
foo/
__init__.py
test_view.py
bar/
__init__.py
test_view.py
#html(</div>)
** テストコードの基本系 [#kcb84353]
#html(<div style="padding:0 40px 0 10px; background:#efefef;border: 1px solid #333;display:inline-block;vertical-align:top;">)
setup.py
src/
mypkg/
__init__.py
app.py
view.py
tests/
__init__.py
foo/
__init__.py
test_view.py
bar/
__init__.py
test_view.py
#html(</div>)
*** セットアップコードと conftest.py [#q5d6c439]
#html(<div style="padding-left:10px;">)
#html(</div>)
** セットアップコードの書き方 [#q5d6c439]
#html(</div>)
// ファイル/フォルダ構成
** テストコードの基本形 [#kcb84353]
#html(<div style="padding-left:10px;">)
以下の命名規則に従っていれば、テストコードとして認識される。
- ファイル名のプレフィックスに test_ または サフィックスに _test を付与する。
- テストメソッドのプレフィックスに test_ または サフィックスに _test を付与する。
例)
#mycode2(){{
def test_sample_function1():
print("test_sample_function1")
assert True
def test_sample_function2():
print("test_sample_function2")
assert False
class TestSampleClass():
def test_class_function():
print("test_class_function1")
assert True
}}
#html(</div>)
// テストコードの基本形
** xxxxxxxxxxx [#jeb0588f]
** assert の書き方 [#x11919d2]
#html(<div style="padding-left:10px;">)
https://qiita.com/mitamura_kei/items/e51c604155633ccd33dd
setup処理からparameterを引き渡したい
テストケースごとに固有の前後処理をしたい
https://docs.pytest.org/en/latest/assert.html
*** 基本形 [#t6cbd22c]
#html(<div style="padding-left:10px;">)
#mycode2(){{
assert 条件(※) [, エラーメッセージ]
}}
※ True の時に成功となる。
#html(</div>)
*** 文字列マッチング [#w2ac605a]
#html(<div style="padding-left:10px;">)
#mycode2(){{
assert 'match string' in str(result)
}}
#html(</div>)
*** 例外を期待する場合 [#v34ef498]
#html(<div style="padding-left:10px;">)
#mycode2(){{
with pytest.raises(ZeroDivisionError):
1 / 0
}}
&br;
#mycode2(){{
with pytest.raises(ValueError, match=r'.* 123 .*'):
myfunc()
}}
#html(</div>)
#html(</div>)
// assert の書き方
** fixtureの利用(テスト前後に行う処理) [#v69ba4d3]
#html(<div style="padding-left:10px;">)
fixtureを使用してテストの前後に行う処理を指定する事ができる。
*** scope について [#q71d9faf]
#html(<div style="padding-left:10px;">)
#html(<div style="display:inline-block;vertical-align:top">)
fixture に scope を指定する事で、どの単位の前処理、後処理なのか明示する事ができる。
|scope|説明|h
|session|テスト全体の前後処理|
|function|各テストfunction毎に行う前後処理|
|class|各テストclass毎に行う前後処理|
|module|各テストモジュール毎に行う前後処理|
|package|各テストパッケージ毎に行う前後処理|
#html(</div>)
#html(<div style="display:inline-block;vertical-align:top;padding-left:20px;">)
例)
#mycode2(){{
import pytest
@pytest.fixture(scope='session', autouse=True)
def session_fixture():
print("テスト全体の前処理")
yield
print("テスト全体の後処理")
@pytest.fixture(scope='module', autouse=True)
def module_fixture():
print("モジュールの前処理")
yield
print("モジュールの後処理")
@pytest.fixture(scope='class', autouse=True)
def class_fixture():
print("クラスの前処理")
yield
print("クラスの後処理")
@pytest.fixture(scope='function', autouse=True)
def function_fixture():
print("関数の前処理")
yield
print("関数の後処理")
}}
#html(</div>)
#html(</div>)
// scope について end
*** yield について [#x1f624c2]
#html(<div style="padding-left:10px;">)
fixture 内に yield を記述する事で、テスト後に行う処理を記述する事が出来る。
yield が記述されていないと、全てテストの前に実行されてしまうので注意が必要。
#mycode2(){{
@pytest.fixture(scope='function', autouse=True)
def module_fixture():
print("関数の前処理")
yield
print("関数の後処理")
}}
#html(</div>)
*** fixture からパラメータを受け取る [#ta69f232]
#html(<div style="padding-left:10px;">)
yield に引数を付けて実行すると、テストコード側で yield の引数を受け取る事ができる。
セットアップコードで生成したインスタンス等を利用してテストコードを実行したい場合等に利用できる。
#mycode2(){{
import pytest
import datetime
@pytest.fixture(scope='module', autouse=True)
def module_fixture1():
print("モジュールの前処理")
now = datetime.datetime.now()
yield {'now': now}
print("モジュールの後処理")
def test_fixture_param(module_fixture1): # 引数に対象のfixtureの関数名を指定する
#print(f'now: {module_fixture1["now"]')
now = module_fixture1['now']
print(f'now: {now}')
}}
#html(</div>)
*** 実行する fixture をテストコード毎に指定する [#l55b07d9]
#html(<div style="padding-left:10px;">)
以下の例では、test_fixture_sample2 の時のみ、前処理、後処理として fixture_for_sample2 が実行される。
#mycode2(){{
import pytest
@pytest.fixture
def fixture_for_sample2():
print("サンプル2用の前処理")
yield
print("サンプル2用の後処理")
def test_fixture_sample1():
print('test_fixture_sample1')
def test_fixture_sample2(fixture_for_sample2): # 引数に実行するfixtureを指定する
print('test_fixture_sample2')
def test_fixture_sample3():
print('test_fixture_sample3')
}}
#html(</div>)
** fixtureを使いこなす [#i35963cf]
#html(</div>)
// テストの前後に行う処理
** mark の利用 [#b678c566]
#html(<div style="padding-left:10px;">)
*** 同じテストを値を変えて行う [#gfdb5035]
#html(<div style="padding-left:10px;">)
pytest.mark.parametrize を使用すると、同じメソッドのテストを引数を得変えてテストする事ができる。
#html(<div style="display:inline-block;">)
pytest.mark.parametrize を使用しない場合
#mycode2(){{
import pytest
# テスト用に直書き
def sample_div(a, b):
return a / b
def test_sample_div():
assert sample_div(10, 5) == 2
assert sample_div(1, 1) == 1
assert sample_div(10, 1) == 10
with pytest.raises(ZeroDivisionError):
sample_div(a, b)
}}
#html(</div>)
#html(<div style="display:inline-block;padding-left:20px;">)
pytest.mark.parametrize を使用する場合
#mycode2(){{
import pytest
# テスト用に直書き
def sample_div(a, b):
return a / b
@pytest.mark.parametrize('a, b, expected', [
(10, 5, 2),
(1, 1, 1),
(10, 1, 10),
(10, 0, ZeroDivisionError),
])
def test_sample_div(a, b, expected):
if type(expected) == int:
assert sample_div(a, b) == expected
else:
with pytest.raises(expected):
sample_div(a, b)
}}
#html(</div>)
#html(</div>)
*** テストをグループ化する [#sc0ef4e4]
#html(<div style="padding-left:10px;">)
pytest.mark に続いて任意の名前を付けておくと、テストコードをグループ化する事ができ、グループ毎にテストを実行する事ができる。
#mycode2(){{
import pytest
@pytest.mark.service1
def test_sample_func1():
print('test_sample_func1')
@pytest.mark.service1
def test_sample_func2():
print('test_sample_func2')
@pytest.mark.service2
def test_sample_func3():
print('test_sample_func3')
}}
実行例 ( service1 のテストコードのみ実行する )
#myterm2(){{
mytest -m service1 -v
}}
#html(</div>)
#html(</div>)
// mark の利用 end
** Tips [#me3d48f1]
#html(<div style="padding-left:10px;">)
*** 標準出力に表示する [#o9a2ab13]
#html(<div style="padding-left:10px;">)
#myterm2(){{
pytest -s
}}
#html(</div>)
*** 処理時間を計測する [#ic092ee4]
テスト時間を記録したい
#html(<div style="padding-left:10px;">)
#myterm2(){{
pytest --durations=0
pytest -v --duration=0
}}
#html(</div>)
*** 前処理と後処理 [#zc0f5637]
@pytest.fixture
*** 同じテストを値を変えて行う [#gfdb5035]
@pytest.mark.parametrize
*** テストをグループ化する [#sc0ef4e4]
@pytest.mark
テストケース別に結果を表示する
*** テストケース別に結果を表示する [#s0422d2a]
#html(<div style="padding-left:10px;">)
#myterm2(){{
pytest -v
}}
#html(</div>)
前回NGだったケースだけテストする
*** 前回NGだったケースだけテストする [#l20f53f6]
#html(<div style="padding-left:10px;">)
#myterm2(){{
pytest -v --lf
}}
#html(</div>)
前回NGだったケースからテストする
*** 前回NGだったケースからテストする [#bfed747f]
#html(<div style="padding-left:10px;">)
#myterm2(){{
pytest -v --ff
}}
#html(</div>)
遅いテストケースを見つける
*** 遅いテストケースを見つける [#q5255543]
#html(<div style="padding-left:10px;">)
#myterm2(){{
pytest -v --duration=5
}}
#html(</div>)
テストログをテキストファイルに出力する
*** テストのログをファイルに出力する [#sf30545f]
#html(<div style="padding-left:10px;">)
#myterm2(){{
pytest -v --result-log=tests/log.txt
}}
#html(</div>)
カバレッジ計測
https://qiita.com/kg1/items/e2fc65e4189faf50bfe6
*** カバレッジを確認する [#ad801cfa]
#html(<div style="padding-left:10px;">)
#myterm2(){{
pytest -v --cov=ディレクトリ
}}
#html(</div>)
*** HTML形式のカバレッジレポートを出力する [#g6612da7]
#html(<div style="padding-left:10px;">)
#myterm2(){{
pytest -v --cov=ディレクトリ --cov-report=html
}}
#html(</div>)
コマンドラインでカバレッジを確認
pytest -v --cov=ディレクトリ
コマンドラインで網羅できなかったコード行を知りたい
*** テストで実行されなかった行を表示する [#vbe41a0d]
#html(<div style="padding-left:10px;">)
#myterm2(){{
pytest -v --cov=ディレクトリ --cov-report=term-missing
}}
#html(</div>)
HTML形式のカバレッジレポートを出力する
pytest -v --cov=ディレクトリ --cov-report=html
#html(</div>)
// tips end