目次

前のトピックへ

使用方法とテスト実行

次のトピックへ

プロジェクトの例

優れたインテグレーションプラクティス

仮想環境での作業

virtualenv 環境を構築して、 pytest パッケージとその他に依存するアプリケーションをインストールするのに easy_install (または pip) を使うことをお奨めします。 tox という、複数の依存設定や Python インタープリターに対して自動的にテストを実行する便利なツールがあります。

tox と継続的インテグレーションサーバーの利用

もし頻繁にコードを一般向けにリリースするなら、virtualenv のテスト自動化とその pytest サポート を行う tox を調べてみたくなるでしょう。基本的な考え方は、 --junitxml=PATH オプションにより JUnitXML ファイルを生成します。そして Jenkins のような継続的インテグレーションサーバーがそのファイルを取得してレポートを生成します。

単独実行できる py.test スクリプトの作成

あなたがメンテナーまたはアプリケーション開発者で、他の人にも簡単にテストを実行させたいなら、単独で実行できる “py.test” スクリプトを作成できます:

py.test --genscript=runtests.py

基本的に py.test スクリプトと完全に同機能をもつ runtests.py スクリプトを生成します。このスクリプトは Python2 と Python3 においても修正せず実行できます。このスクリプトをダウンロードして、例えば、次のように実行してくださいと伝えれば良いです:

python runtests.py

python setup.py test による distutils との連携

プロジェクトベースの distutils または setuptools でテスト実行を連携できます。単独で実行できる py.test スクリプトを生成するには genscript メソッド を使ってください:

py.test --genscript=runtests.py

このスクリプトを配布物の一部にして setup.py ファイルに次のコードを追加します:

from distutils.core import setup, Command
# setuptools からもインポートできます

class PyTest(Command):
    user_options = []
    def initialize_options(self):
        pass
    def finalize_options(self):
        pass
    def run(self):
        import sys,subprocess
        errno = subprocess.call([sys.executable, 'runtest.py'])
        raise SystemExit(errno)
setup(
    #...,
    cmdclass = {'test': PyTest},
    #...,
)

ここで次のように実行します:

python setup.py test

これは runtest.py を使ってテストを実行します。このように、単独で実行できる py.test スクリプトは、そのテストコマンドを呼び出すために依存パッケージをインストールする必要がありません。さらにテストディレクトリやその他のオプションなど、subprocess.call に追加の引数として渡せます。

setuptools/distribute のテストコマンドとの組み合わせ

setuptools/distribute は、テストに必要なパッケージ要件から pytest を実行するテストコマンドをとても簡単に拡張できる tests_require に対応しています:

from setuptools.command.test import test as TestCommand

class PyTest(TestCommand):
    def finalize_options(self):
        TestCommand.finalize_options(self)
        self.test_args = []
        self.test_suite = True
    def run_tests(self):
        # 外部で egg を読み込ませたくないならここでインポートしてください
        import pytest
        pytest.main(self.test_args)

setup(
    #...,
    tests_require=['pytest'],
    cmdclass = {'test': pytest},
    )

ここで次のように実行します:

python setup.py test

必要に応じて py.test をダウンロードしてから、期待した通りに py.test を実行します。

Python テスト探索の規約

py.test は次のテスト探索標準を実装します:

  • コレクションは、ディレクトリ、ファイル名、テスト ID といった最初に与えたコマンドライン引数から開始する
  • norecursedirs に一致しない限り、ディレクトリを再帰的に探索する
  • package name でインポートされる test_*.py または *_test.py ファイル
  • Test という接頭辞をもつテストクラス (__init__ メソッドをもたない)
  • test_ という接頭辞をもつテスト関数やメソッドがテスト項目になる

テスト探索をカスタマイズする方法の例は 標準的な (Python) テスト探索の変更 を参照してください。

Python モジュール内では、py.test も標準ライブラリの unittest.TestCase のサブクラス化を使ってテストを探索します。

テストレイアウト選択とインポートルール

py.test は一般的なテストレイアウトに対応しています:

  • アプリケーション内に test ディレクトリを配置しています。(ユニット) テストを保持して実際にテストされたコードを一緒にしておくのに役立ちます:

    mypkg/
        __init__.py
        appmodule.py
        ...
        test/
            test_app.py
            ...
  • テストをアプリケーションコードの外部に配置しています。多くの機能テストがある、または実際のアプリケーションコードからテストを分離して保持したいときに役立ちます:

    mypkg/
        __init__.py
        appmodule.py
    tests/
        test_app.py
        ...

どちらの場合も、普通に mypkg がインポートできることを保証する必要があります。例えば、setuptools の python setup.py develop メソッドを使います。

次のようにテストを実行できます:

py.test tests/test_app.py       # 外部のテストディレクトリ
py.test mypkg/test/test_app.py  # 内部のテストディレクトリ
py.test mypkg                   # テストディレクトリ配下にある全てのテストを実行
py.test                         # カレントディテクリ配下にある全てのテストを実行
...

ノート

py.test がファイルシステムを再帰的に辿って “a/b/test_module.py” テストファイルを検出する場合、インポート名を次のようにして決定します。

  • basedir を検出する – これは __init__.py を含まない最初の “upward” (ルートに向かう) ディレクトリです。 ab のディレクトリ両方に __init__.py を含む場合、basedir は a の親ディレクトリになります
  • テストモジュールを完全修飾インポート名でインポートできるようにするために sys.path.insert(0, basedir) を実行します
  • パス区切り文字 / を ”.” に変換することで決まる import a.b.test_module を行う、つまりインポート名に直接ディレクトリやファイル名を対応付ける規約に従わないといけません

この少し進化したインポートテクニックを使う理由は、巨大なプロジェクトでは複数のテストモジュールがお互いにインポートする可能性があるからです。そして、このように導出されたインポート名の標準化は、テストモジュールを2回インポートしてしまって驚かないようにするのに役立ちます。