その他

【Python】おすすめライブラリ紹介【pytest】

その他
この記事は約9分で読めます。

インプルの井上です。本日はPythonのライブラリpytestをご紹介いたします。

pytestは単体テスト(ユニットテスト)を支援するためのモジュールでして、テストの作成と実施を

効率的に行うことができます。ご参考ください。

参照1:https://rinatz.github.io/python-book/ch08-02-pytest/

参照2:https://qiita.com/kg1/items/4e2cae18e9bd39f014d4

テストとは?

そもそもテストとは何なのでしょうか?結論から申し上げるとテストとは

プログラムが想定通りに動作することを確認する作業

のことです。その中でも今回ご紹介する単体テストは、プログラムを組んだモジュールや

コンポーネントといった機能単位の動作などを単体で確認するテストのことを指します。

システム開発における数あるテスト工程の内の一つで、結合テストや総合テスト、

運用テストの前に行われます。詳しくは是非ご自分で調べてみてください。

pytestの使い方

では準備〜実装〜実行までサンプルコードを交えながらpytestの使い方を説明していきます。

準備

まずはpytestをインストールします。

pip install pytest

今回のフォルダ構成と詳細です。

  • mainフォルダに コード (テスト対象の関数) を配置
  • testsフォルダに テストコード を配置
プロジェクト名
 ├─main
 |  ├─ __init__.py
 |  ├─ calc.py
 |  └─ say.py
 └─tests
    ├─ __init__.py
    ├─ test_calc.py
    └─ test_say.py

テストコードのファイル名は「test_」で始めるのがpytestの規則となります。

test_とすることでpytestが明示的にそのファイルをテスト対象と認識してくれます。

テストの書き方

コード

# calc.py

class Calc: 

    def __init__(self, a, b):
        self.a = a
        self.b = b

    def add(self):
        return self.a + self.b

    def dif(self):
        return self.a - self.b

    def seki(self):
        return self.a*self.b

    def shou(self):
        return self.a/self.b
# say.py

class Foo:

    def say(self):
        return 'foo'

    def say2(self):
        return 'foo2'

class Hoge:

    def say(self):
        return 'hoge'

    def say2(self):
        return 'hoge2'

テストコード

パス設定が必要な場合はPYTHONPATHを追加するなど適宜対応してください。

テストケースの名前(定義関数名)は処理が名前から読み取れるように定義しましょう。

# test_calc.py

from main.calc import Calc

def test_add_01():
    assert Calc(9,2).add() == 11

def test_add_02():
    assert Calc(-9,2).add() == -7

def test_dif_01():
    assert Calc(9,2).dif() == 7

def test_dif_02():
    assert Calc(-9,2).dif() == -11

def test_seki_01():
    assert Calc(9,2).seki() == 18

def test_seki_02():
    assert Calc(-9,2).seki() == -18

def test_shou_01():
    assert Calc(9,2).shou() == 4.5

def test_shou_02():
    assert Calc(-9,2).shou() == -4.5
# test_say.py

from main.say import Foo,Hoge

def test_foo_say():
    assert Foo().say() == 'foo'

def test_foo_say2():
    assert Foo().say2() == 'foo'  #テストエラーの出力確認。

def test_hoge_say():
    assert Hoge().say() == 'hoge'

def test_hoge_say2():
    assert Hoge().say2()== 'hoge2'

テスト実行

すべてのテストコードを実施

pytest

指定テストコードのみ実施

pytest tests/test_calc.py

実行結果

パターン1

実行コマンド

pytest

実行結果

study_pytest>pytest
============================= test session starts =============================
platform win32 -- Python 3.11.0, pytest-3.4.2, py-1.5.2, pluggy-0.6.0
rootdir: C:\Users\xxx\python\study_pytest, inifile:
collected 12 items

tests\test_calc.py ........                                              [ 66%]
tests\test_say.py .F..                                                   [100%]

================================== FAILURES ===================================
________________________________ test_foo_say2 ________________________________

    def test_foo_say2():
>       assert Foo().say2() == 'foo'
E       AssertionError: assert 'foo2' == 'foo'
E         - foo2
E         ?    -
E         + foo

tests\test_say.py:7: AssertionError
===================== 1 failed, 11 passed in 0.13 seconds =====================

パターン2

実行コマンド

・テストケースごとに PASSED/FAILED が表示される。

pytest -v

実行結果

study_pytest>pytest -v
============================= test session starts =============================
platform win32 -- Python 3.11.0, pytest-3.4.2, py-1.5.2, pluggy-0.6.0 -- c:\users\xxx\appdata\local\programs\python\python311\python.exe
cachedir: .pytest_cache
rootdir: C:\Users\xxx\Documents\python\study_pytest, inifile:
collected 12 items

tests/test_calc.py::test_add_01 PASSED                                   [  8%]
tests/test_calc.py::test_add_02 PASSED                                   [ 16%]
tests/test_calc.py::test_dif_01 PASSED                                   [ 25%]
tests/test_calc.py::test_dif_02 PASSED                                   [ 33%]
tests/test_calc.py::test_seki_01 PASSED                                  [ 41%]
tests/test_calc.py::test_seki_02 PASSED                                  [ 50%]
tests/test_calc.py::test_shou_01 PASSED                                  [ 58%]
tests/test_calc.py::test_shou_02 PASSED                                  [ 66%]
tests/test_say.py::test_foo_say PASSED                                   [ 75%]
tests/test_say.py::test_foo_say2 FAILED                                  [ 83%]
tests/test_say.py::test_hoge_say PASSED                                  [ 91%]
tests/test_say.py::test_hoge_say2 PASSED                                 [100%]

================================== FAILURES ===================================
________________________________ test_foo_say2 ________________________________

    def test_foo_say2():
>       assert Foo().say2() == 'foo'
E       AssertionError: assert 'foo2' == 'foo'
E         - foo2
E         ?    -
E         + foo

tests\test_say.py:7: AssertionError
===================== 1 failed, 11 passed in 0.18 seconds =====================

最後に

いかがだったでしょうか?今回は紹介したテストコマンドはほんの一部です。

他にはNGのテストから再実行、テスト結果をログ出力、テスト時間の計測や遅いテストの抽出

など多くのテストコマンドがあります。標準設定ではないのですが、カバレッジをhtml出力して

計測するなどもできるので是非活用してみてください。