メインコンテンツへスキップ

pytestを実行するためのPythonパッケージのディレクトリ構成

·1661 文字·4 分
目次

はじめに
#

Pythonでパッケージを作成するとき、pytestによる自動テストに向いたディレクトリ構成をサンプルと合わせて解説します。pytestはPythonコードのテストを自動化するライブラリです。

pytestの基本的な使い方は以下の記事にまとめています。 pytestを使ったPythonのテスト – Helve Tech Blog

この記事で検証した環境は以下の通りです。

  • OS: Windows 10 Home
  • Python 3.11.6
  • pytest 7.4.3

以下のコマンドでpytestをインストールします。

> pip install pytest

この記事はPython Advent Calendar 2023 (Qiita) 11日目の記事です。

ディレクトリ構成
#

ディレクトリ構成の例を以下に示します。<my_project>は任意のパッケージ名に変更します。

<my_project>/
    <my_project>/
        __init__.py
        <my_project>.py
        *.py
    tests/
        __init__.py
        test_<my_project>.py
        test_*.py

<my_project>/<my_project>/フォルダの下にパッケージのプログラムを置きます。また、<my_project>/tests/フォルダの下には、テスト用のプログラムを置きます。 pytestを実行するに当たって、testsフォルダ直下の__init__.pyが必要になります。また、*.pytest_*.pyは、必要に応じて追加する任意の名前のモジュールです。

パッケージの例
#

簡単なパッケージを例として示します。ここではパッケージ名をfooとします。

foo/
    foo/
        __init__.py
        foo.py
    tests/
        __init__.py
        test_foo.py

4つのファイルの内容を以下に示します。

foo/foo/__init__.pyの書き方はいくつか方法がありますが、以下とします。このファイルによって、プロジェクトのルートフォルダfoo/直下でpytestコマンドを実行したときに、foo/foo/foo.pyをインポートできるようになります。

from .foo import *

foo/foo/foo.py(実際のプログラム)の中身は以下とします。2つの変数の和を返す単純な関数です。

def add(x, y):
    return x+y

一方、foo/tests/__init__.pyの中身は空とします。このファイルを置くことによって、pytestがtestsフォルダをパスに追加してくれます。

foo/tests/test_foo.pyfoo.pyをテストするためのファイルです。ファイル名の先頭にtestを付けることによって、テスト内容を定義したファイルであることをpytestに認識させます。中身は以下とします。import fooを記述して、foo.pyをインポートすることに注意してください。

import foo

def test_add():
    assert foo.add(1, 2) == 3

pytestの実行
#

pytestはコマンドプロンプトまたはPowerShellから実行します。プロジェクトのルートフォルダfoo/直下で、pytestコマンドを実行します。

foo> pytest
======================= test session starts ========================
platform win32 -- Python 3.11.6, pytest-7.4.3, pluggy-1.3.0
rootdir: /XXX/foo/
plugins: html-4.1.1, metadata-3.0.0
collected 1 item

tests\test_foo.py .                                           [100%]

======================== 1 passed in 0.03s =========================

pytest実行時のエラーについて
#

pytestコマンドを実行するときに発生しがちなエラーを以下に示します。

AttributeError
#

foo/foo/__init__.pyを置いていない場合や、このファイルの中身が空の場合、以下のようにAttributeErrorが発生します。foo/foo/__init__.pyのファイル名や中身が間違っていないか等、確認して下さい。

foo> pytest
======================= test session starts ========================
platform win32 -- Python 3.11.6, pytest-7.4.3, pluggy-1.3.0
rootdir: /XXX/foo
plugins: html-4.1.1, metadata-3.0.0
collected 1 item

tests\test_foo.py F                                           [100%]

============================= FAILURES =============================
_____________________________ test_add _____________________________

    def test_add():
>       assert foo.add(1, 2) == 3
E       AttributeError: module 'foo' has no attribute 'add'

tests\test_foo.py:6: AttributeError
===================== short test summary info ======================
FAILED tests/test_foo.py::test_add - AttributeError: module 'foo' has no attribute 'add'
======================== 1 failed in 0.20s =========================

ModuleNotFoundError
#

foo/tests/__init__.pyを置いていない場合、以下のようにModuleNotFoundErrorが発生します。foo/tests/__init__.pyのファイル名が間違っていないか等、確認して下さい。

foo> pytest
======================= test session starts ========================
platform win32 -- Python 3.11.6, pytest-7.4.3, pluggy-1.3.0
rootdir: /XXX/foo/
plugins: html-4.1.1, metadata-3.0.0
collected 0 items / 1 error

============================== ERRORS ==============================
________________ ERROR collecting tests/test_foo.py ________________
ImportError while importing test module 'XXX/foo/tests/test_foo.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
C:\Users\helve\AppData\Local\Programs\Python\Python311\Lib\importlib\__init__.py:126: in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
tests\test_foo.py:2: in <module>
    import foo
E   ModuleNotFoundError: No module named 'foo'
===================== short test summary info ======================
ERROR tests/test_foo.py
!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!
========================= 1 error in 0.16s =========================

参考
#

Helve
著者
Helve
関西在住、電機メーカ勤務のエンジニア。X(旧Twitter)で新着記事を配信中です

関連記事

pytestのテスト結果をHTMLファイルに出力する
·1256 文字·3 分
pytestとpytest-htmlを使って、Pythonコードのテスト結果をHTML形式で保存します。
pytestを使ったPythonのテスト
·1855 文字·4 分
pytestによる基本的なテストの実行方法と、複数のテストケースを記述する方法を解説します。
conda環境ではなくpip環境にspyderをインストールする【Windows環境】
··1669 文字·4 分
AnacondaやMinicondaなどのconda環境を使用せずに、pipのみの環境でPythonの統合開発環境Spyderを使用できるようにします。
CasADiのOptiスタックでcallback関数を使って解の推移を取得する
·773 文字·2 分
最適化フレームワークCasADiのOptiスタックでcallback関数を使って、最適化ソルバの各反復における解の推移を取得する方法をまとめました。
CasADiで最適化の変数と制約をベクトル化する
·1082 文字·3 分
最適化フレームワークCasADiのOptiスタックで、多変数の最適化を簡潔に記述するため、変数と制約をベクトル化する方法をまとめました。
JupyterLab, Jupyter Notebookを任意のディレクトリで起動する【Windows環境】
·1354 文字·3 分
JupyterLabまたはJupyter Notebookを任意のディレクトリで起動する方法を説明します。Windows環境でAnaconda PowerShell Promptを使用します。