JavaScriptを有効にしてください

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

 ·   4 min read

※記事内に商品プロモーションを含むことがあります。

はじめに

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

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

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

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

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

1
> 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をインポートできるようになります。

1
from .foo import *

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

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

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

1

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

1
2
3
4
import foo

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

pytestの実行

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

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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のファイル名や中身が間違っていないか等、確認して下さい。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
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のファイル名が間違っていないか等、確認して下さい。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
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
WRITTEN BY
Helve
関西在住、電機メーカ勤務のエンジニア。X(旧Twitter)で新着記事を配信中です

サイト内検索