Masayan tech blog.

  1. ブログ記事一覧>
  2. pytestでPythonスクリプトの単体テストを行う

pytestでPythonスクリプトの単体テストを行う

公開日

環境

  • Windows 10
  • Python 3.10.1
  • VSCode

pytestとは?

Pythonで書いたプログラムをテストするためのフレームワーク

導入手順

pip等でpytestをインストールする

pip install pytest

実装

  • testで始まる名前の関数が自動的にテスト対象になる
  • 各関数の中で、結果がTrueになるべき式をassert文で記述する
from shared.Application.check_is_valid_url_service import CheckIsValidUrlService
from shared.Domain.xurl import XUrl

def test_任意のurlが有効かどうかチェックできること():
    x_url = XUrl("https://maasaablog.com/")
    is_valid_url = CheckIsValidUrlService().execute(xurl=x_url)
    assert is_valid_url

def test_無効なurlの場合はFalseを返すこと():
    x_url = XUrl("https://hfefejjekfogehoge.com/")
    is_valid_url = CheckIsValidUrlService().execute(xurl=x_url)
    assert not is_valid_url

テストの実行

引数にスクリプトファイル名を指定してpytestコマンドを実行

pytest tests/test_check_is_valid_url_service.py

========================== 2 passed in 0.48s ===========================
  • 引数を省略すると、カレントディレクトリにあるtest_*.pyという名前のスクリプトが自動的にテスト対象になる
  • 以下のように実行すると、testsディレクトリ直下のtest_*.pyが実行される
pytest tests

その他のTips

異常系のテスト

  • with pytest.raises(Exception) を使うと、with文の中で指定した例外が送出されるかどうかを検証可能
  • 以下はEnumクラスに存在しない値を指定した時に、ValueErrorが生じるかどうかの検証
import pytest
from shared.Enums.ScrapingType import ScrapingType

def test_存在しない種別の場合は例外():
    with pytest.raises(ValueError):
        ScrapingType(99) == ScrapingType.SELENIUM

fixture

  • 他の言語のテスト用フレームワークでいうところのsetupのようなもの
  • 引数にfixtureとして定義したメソッド名を書くことで、各テスト開始直前に実行される

tests\test_str.py

import pytest
from shared.Domain.xstr import XStr

@pytest.fixture
def setuped_xstr():
    xstr = XStr("masayan")
    return xstr

def test_対象の文字列に特定の文字が含まれているかチェックできること_含まれていない場合(setuped_xstr):
    assert setuped_xstr.is_contain("hoge") == False

def test_対象の文字列に特定の文字が含まれているかチェックできること_含まれている場合(setuped_xstr):
    assert setuped_xstr.is_contain("yan") == True
  • teardown(テスト終了後に行う特定の処理)は、以下のようにfixureの中でyieldを使用することで実現可能
@pytest.fixture
def setuped_xfile() -> None:
    x_url = XUrl(href="https://www.home-movie.biz/mov/hts-samp001.mp4")
    x_file = XFile(x_url)
    yield x_file
    x_file_system_path = XFileSystemPath(XStr("tests"))
    downloaded_file_path = x_file_system_path.join(x_file.get_file_name())
    downloaded_file_path.delete()

yield以降の処理がテスト終了後に必ず実行されます。また、setupの関数が返り値を必要とする場合は、returnは不要で、yield 返り値のように記述すればOKです

また、fixtureのスコープを指定することも可能です

@pytest.fixture(scope="module")
def sut():
  ・・・

fixtureのスコープは以下の通りです

  • function
    テストケースごとに1回実行、未指定の場合のデフォルト
  • class
    テストクラス全体で1回実行、テストメソッドがクラスに含まれていなくても実行される。
  • module
    テストファイル全体で1回実行
  • session
    テスト全体で1回実行

pytest.fixtureで引数を渡すには以下のように(関数を返すように)します

@pytest.fixture
def something_fixture():
  def func(a, b):
    return a*b
return func

def test_hoge(something_fixture):
 ・・・・

スキップしたいテスト

スキップしたいテストの場合は、@pytest.mark.skipifを指定すればOK

@pytest.mark.skipif(True, reason="[スキップする理由などをここに記載する]")
def test_特定のセルを取得できる(setuped_worksheet: XWorksheet):
    assert setuped_worksheet.get_cell(2, 1) == "Python"

まとめ

いかがでしたでしょうか。本記事では、pytestでPythonスクリプトの単体テストを行う方法について紹介しています。ぜひ参考にしてみてください