JavaScriptを有効にしてください

pytestを使ってみる①【準備・実行編】(python3.9.0)

 ·  ☕ 5 分で読めます · 👀... ページ閲覧数

背景

最近仕事でPytestを使ったUnitTestをかく機会があったので使い方を忘れないようにメモ。参考にもあるが、こちらのサイトがすごくわかりやすかった。
感謝。。。!

ちなみに実行環境はmacで利用しているpythonのバージョンはpython3.9.0です。

pytestとは

Python製のテストフレームワークの一つ。似たようなものにunittestがある。unittestは標準で使えるライブラリだが、pytestはライブラリのインストールが必要となる。
カバレッジを算出したり、結果をhtmlファイルに出力することができて便利。

基本的な使い方

インストール方法

pipコマンドからインストールが可能

1
$ pip install pytest

ディレクトリ構成

.
├── src
│   ├── main.py
│   └── util
│       ├── __init__.py
│       └── util.py
└── tests
    ├── __init__.py
    ├── conftest.py
    └── test_main.py

srctestsは分けた方が管理しやすいので分割。
CIなどでtestを実行した時にPATH通るよう、conftest.pyは以下のように書いた。

1
2
3
4
import sys
import os

sys.path.append(os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + "/../src/"))

実行したいテストのファイル名はtest_xxx.pyの名前をつけると良い。自動でテスト対象としてみなしてくれる。

基本的な書き方と実行方法

基本的な書き方

1
2
3
4
5
6
7
def test_main_01():
    # 試験実施
    a = 1
    b = 1

    # 結果の検証
    assert a == b

実行方法

テストケースをまとめて実行する

root(今回の例では.)で実行。
tests/配下のテストが実行される。

1
$ pytest .
試験ケースを指定して実行する

例えばtest_main.pyファイル内のtest_main_01を実行したい時は以下のように指定する。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
$ pytest tests/test_main.py::test_main_2

# "-v"オプションをつけると指定の仕方を確認することができて便利。
$ pytest . -v
======================================================================================== test session starts ========================================================================================
cachedir: .pytest_cache
rootdir: /Users/hogehoge/
collected 2 items

tests/test_main.py::test_main_01 PASSED  [ 50%]
tests/test_main.py::test_main_02 PASSED  [100%]

そのほか便利な機能

カバレッジを出したい時

pytest-covを利用する。

1
$ pip install pytest-cov

インストール後、pytestコマンドにオプションをつけることでカバレッジを表示することが可能になる。
今回の例ではsrc/配下にソースを格納しているためsrcを設定した。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
# "-v"オプションをつけると一つ一つの試験結果が表示され結果が見やすくなる
$ pytest -v --cov=src
======================================================================================== test session starts ========================================================================================
cachedir: .pytest_cache
rootdir: /Users/hogehoge/
plugins: cov-2.10.1
collected 2 items

tests/test_main.py::test_main_1 PASSED  [ 50%]
tests/test_main.py::test_main_2 PASSED  [100%]

---------- coverage: platform darwin, python 3.9.0-final-0 -----------
Name                   Stmts   Miss  Cover
------------------------------------------
src/main.py                9      1    89%
src/util/__init__.py       0      0   100%
src/util/util.py           2      0   100%
------------------------------------------
TOTAL                     11      1    91%

========================================================================================= 2 passed in 0.08s =========================================================================================

カバレッジをHTMLで表示したい

テストできているところ / いないところを確認する
$  pytest --cov=src --cov-report=html

コマンドを実行したフォルダ配下にhtmlcovフォルダが作成されている。フォルダ内のindex.htmlを開くとファイルごとのカバレッジが表示されている。それぞれリンクになっていて、クリックするとテストができているところとそうでないところを確認することができる。

pytest_1

src/main.pyをクリックするとどこがテストできていないのかすぐわかる。

pytest_2

テスト結果のサマリをHTMLで表示する

pytest-htmlをインストールする。

1
2
3
4
5
6
# インストール
$ pip install pytest-html

# 実行方法
# --html=<出力したいファイル名>
$ pytest . --html=cov.html

以下のように試験結果が表示される。

pytest_3

テスト対象外のファイルを設定したい

pytest対象外としたいファイルがある時の設定方法。
rootディレクトリに.coveragercを作成する(今回の例ではsrc, testsフォルダと同じ階層に作成)

.
├── src
├── tests
└── .coveragerc

.coveragercに対象外としたいフォルダやファイル名を記述する。

1
2
3
4
[run]
omit =
   # __init__.py ファイルはテスト対象外とする
   */__init__.py

試験を実行すると設定したフォルダやファイルがテスト対象外となっている。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
$ pytest --cov=src
======================================================================================== test session starts ========================================================================================
platform darwin -- Python 3.9.0, pytest-6.2.1, py-1.10.0, pluggy-0.13.1
rootdir: /Users/hogehoge/
plugins: html-3.1.1, metadata-1.11.0, cov-2.10.1
collected 2 items

tests/test_main.py ..   [100%]

---------- coverage: platform darwin, python 3.9.0-final-0 -----------
Name               Stmts   Miss  Cover
--------------------------------------
src/main.py            9      1    89%
src/util/util.py       2      0   100%
--------------------------------------
TOTAL                 11      1    91%


========================================================================================= 2 passed in 0.09s =========================================================================================

テストで失敗した時にメッセージを出力したい

assert後にカンマ(,)をつけ、その後メッセージを書く。

1
2
3
4
5
6
7
def test_main_failure_1():
    # 試験データ
    a = 1
    b = 5

    # テストに失敗した時メッセージが表示される
    assert a == b, "a not equal b"
 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
pytest tests/test_main_failure.py
======================================================================================== test session starts ========================================================================================
platform darwin -- Python 3.9.0, pytest-6.2.1, py-1.10.0, pluggy-0.13.1
rootdir: /Users/hogehoge/
plugins: html-3.1.1, metadata-1.11.0, cov-2.10.1
collected 1 item

tests/test_main_failure.py F    [100%]

============================================================================================= FAILURES ==============================================================================================
________________________________________________________________________________________ test_main_failure_1 ________________________________________________________________________________________

    def test_main_failure_1():
        # 試験データ
        a = 1
        b = 5

        # 想定値
>       assert a == b, "a not equal b"
E       AssertionError: a not equal b
E       assert 1 == 5

tests/test_main_failure.py:7: AssertionError
====================================================================================== short test summary info ======================================================================================
FAILED tests/test_main_failure.py::test_main_failure_1 - AssertionError: a not equal b
========================================================================================= 1 failed in 0.05s =========================================================================================

どんなデータが入っていてもassertの結果をTrueにしたい時

pytestではなく、標準ライブラリとして用意されているunittestANYを利用することで可能。
滅多に使うケースはないが、知っているとすごく便利。例えばmockにした関数に渡された引数のassertをしたいがその一部が実行時点のタイムスタンプやランダム値など予測不能な値である場合に対象項目の予測値にANYを利用する。

どんなデータでも通ってしまうので利用する時は注意が必要。。

1
2
3
4
5
6
7
8
from unittest.mock import ANY

def test_main_any_1():
    # 試験データ
    a = None

    # こんなassertも問題なく通る
    assert a == ANY

参考

大変参考にさせていただきました。ありがとうございます。

共有

BAMBi
著者
BAMBi
サーバサイド~インフラがメインでフロントも好きです。趣味はアニメ鑑賞、ゲーム、つまみ細工です。