Pytest 簡単な使い方

Pythonのテスティングフレームワークに pytest,unittest がある。 とりあえず pytest を使う感じで書く。 他には docstring に記述するテストがあり doctest と呼ぶ。 これはドキュメントでもあり簡潔に使い方を伝える役目も持ってる。

pytestで走らせる

まず簡単なテストを書いておく。

1
2
def test_foo():
  assert false

この例では pytest は全く使っていない Python 標準の assert によって例外を投げているだけ。

pytest を使うとカレントディレクトリ配下のテストを走らせることができる。 またファイルを指定して使うこともできる。

1
2
pytest
pytest -q test_foo.py

ioprobe では下のようになる。 ちゃんと通過する。(たいしたことはしてない)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
$ cd ioprobe
$ pytest -v
=========================================================================================== test session starts ============================================================================================
platform linux2 -- Python 2.7.5, pytest-3.5.0, py-1.5.3, pluggy-0.6.0 -- /bin/python
cachedir: .pytest_cache
rootdir: /home/vagrant/works/ioprobe, inifile:
plugins: pep8-1.0.6, flakes-2.0.0
collected 6 items

src/ioprobe/test_ioprobe.py::test_io_path PASSED                                                                                                                                                     [ 16%]
src/ioprobe/test_ioprobe.py::test_header PASSED                                                                                                                                                      [ 33%]
src/ioprobe/test_ioprobe.py::test_report_items PASSED                                                                                                                                                [ 50%]
src/ioprobe/test_ioprobe.py::test_delta PASSED                                                                                                                                                       [ 66%]
src/ioprobe/test_ioprobe.py::test_DeltaReport_header PASSED                                                                                                                                          [ 83%]
src/ioprobe/test_ioprobe.py::test_DeltaReport PASSED                                                                                                                                                 [100%]

========================================================================================= 6 passed in 0.03 seconds =========================================================================================

fixtureについて

フレームワークは事前処理などを簡単に実施する方法、 fixture を提供している。

fixtureはモジュール,クラス,関数単位で事前・事後処理を定義できる。 fixture の関数名をテスト関数の引数にするとfixtureから値を受け取れる。 自分で定義するときはconftest.pyに集約するのが良いらしい

fixture は標準で準備されてるものがたくさんある。 下は、 tmpdir を受け取っていて名前の通り一時ファイル用のディレクトリのパスが入っている。

1
2
3
def test_needsfiles(tmpdir):
    print(tmpdir)
    assert 0

下のように引数にテスト用のディレクトリが渡されている。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
pytest  -q ./test_foo.py
F                                                                                                                                                                                                    [100%]
================================================================================================= FAILURES =================================================================================================
_____________________________________________________________________________________________ test_needsfiles ______________________________________________________________________________________________

tmpdir = local('/tmp/pytest-of-vagrant/pytest-0/test_needsfiles0')

    def test_needsfiles(tmpdir):
          print(tmpdir)
>         assert 0
E         assert 0

test_foo.py:5: AssertionError
------------------------------------------------------------------------------------------- Captured stdout call -------------------------------------------------------------------------------------------
/tmp/pytest-of-vagrant/pytest-0/test_needsfiles0
1 failed in 0.02 seconds

Mock実装

MonkeyPatchが提供されている。 しかしビルトインオブジェクトのメソッドの差し替えは失敗した。

そのため必要に応じ unittest.mockmock.mock を利用する。 unittest.mock は新しい標準ライブラリで、 mock.mock はバックポートされた実装にあたる。

これらを使っている例を下においておく。 report.start()の中でdatetime.now()が呼ばれているけど入れ替わった。

 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
if sys.version_info.major != 2:
    from unittest.mock import patch
else:
    from mock.mock import patch

def defock_now():
    yield datetime.datetime(2018, 1, 1, 0, 0, 0)
    yield datetime.datetime(2018, 1, 1, 0, 0, 1)
    yield datetime.datetime(2018, 1, 1, 0, 0, 2)

def test_func():
    import ioprobe
    with patch("ioprobe.datetime") as mock_datetime:
        mock_now_generator = mock_now()
        mock_datetime.now = wrapped_next(mock_now_generator)

        report = ioprobe.DeltaReport( ["a", "b"],
                [{"a":0, "b": 0}, {"a": 100, "b": -100}])
        lines = report.start()
        assert wrapped_next(lines)() == r'{"date": "2018-01-01 00:00:01", "a/s": 100.0, "b/s": -100.0}'

def wrapped_next(generator):
    if sys.version_info.major == 2:
        return generator.next
    else:
        return generator.__next__
comments powered by Disqus