LoginSignup
17
19

More than 3 years have passed since last update.

Python+pytestでテスト駆動開発しようとしたらテストの実行で躓いたので解決法をメモ

Last updated at Posted at 2019-08-30

業務でPythonによるテスト駆動開発(TDD)を行うことになり、pytestを用いました。ただ、ディレクトリ構造が正しく認識されないとModuleNotFoundErrorに阻まれて実装が進まなくなってしまうため、備忘録を兼ねてまとめることにしました。

環境構築

pytestのインストールが必要です。下記コマンドの実行でインストールが可能です。

pip install pytest

pytestを毎回手動実行するのは煩雑であるため自動実行できる様にしておこうと思い、pytest-watchもインストールしました。起動しておくとソースが更新される毎に自動でpytestによるテストが実行されます。

pip intall pytest-watch

起動方法

ptw

最も簡単な方法

最も簡単な方法はソースファイルとテストソースを同じディレクトリ内に格納する方法です。これ以降、以下のサンプルソースを使って説明します。空ファイルに近い内容ですが、pytestが実行されているかの確認には使えます。

ソースファイル(hogehoge.py)

class HogeHoge:
    pass

テストソース(test_hogehoge.py)

from hogehoge import HogeHoge

def test_init():
    HogeHoge()

pytestはtest_hogehoge.pyまたはhogehoge_test.pyというファイル名を探して実行される仕様となっているので必ずテストソースのファイル名にはtestの文言を含めましょう。別のターミナルをもう1つ起動しソースが格納されているディレクトリに移動した上でpytest-watchを起動してみましょう。pytestが実行されます。

正常にテストが完了するとこの様な表示が出ます
============================= test session starts ==============================
platform darwin -- Python 3.7.3, pytest-5.1.1, py-1.8.0, pluggy-0.12.0
rootdir: sample-tdd
collected 1 item 
test_hogehoge.py .                                                       [100%]

============================== 1 passed in 0.01s ===============================

ソースディレクトリを切り分ける方法

ソースファイルが少ない時は上記の方法でも良いでしょう。しかし、ソースファイルが増えればソースファイルとテストソースが混在してしまい、目的のソースファイルが探しにくくなります。故にソース用ディレクトリとテスト用ディレクトリに切り分けます。

ディレクトリの切り分け

今回は以下の様なディレクトリ構造にしました。pytestはカレントディレクトリかtestsというディレクトリにテストを探しに行く仕様となっている様なので、testsというディレクトリ名にしています。

sample_tdd/
├── modules
│   └── hogehoge.py
└── tests
    └── test_hogehoge.py

__init__.pyの準備

modulesディレクトリにinit.pyを格納します。記載内容は以下の通りです。

from .hogehoge import HogeHoge

__all__ = [

    'HogeHoge',

]

テストソースの修正

test_hogehoge.pyの内容を以下の様に修正します。

- from hogehoge import HogeHoge
+ from modules.hogehoge import HogeHoge

def test_init():
    HogeHoge()

結果として以下の様なディレクトリ構造になりました。

sample_tdd/
├── modules
│   ├── __init__.py
│   └── hogehoge.py
└── tests
    └── test_hogehoge.py

pytest-watchを実行すると以下の様な結果となりました。

______________ ERROR collecting sample_tdd/tests/test_hogehoge.py ______________
ImportError while importing test module 'sample_tdd/tests/test_hogehoge.py'.
Hint: make sure your test modules/packages have valid Python names.
Traceback:
sample_tdd/tests/test_hogehoge.py:1: in <module>
    from modules.hogehoge import HogeHoge
E   ModuleNotFoundError: No module named 'modules'
!!!!!!!!!!!!!!!!!!! Interrupted: 1 errors during collection !!!!!!!!!!!!!!!!!!!!
=============================== 1 error in 0.05s ==============================

conftest.pyを追加

プロジェクトのrootディレクトリ(この例ではsample_tddディレクトリ)に空ファイルconftest.pyを追加します。追加には以下のコマンドを実行します。これはpytestの仕様上必要なファイルです。

touch conftest.py

ディレクトリ構造は以下の様になりました。

sample_tdd/
├── conftest.py
├── modules
│   ├── __init__.py
│   └── hogehoge.py
└── tests
    └── test_hogehoge.py

pytest-watchの実行結果が以下の様に変わり正常にテストが終了しました。

============================= test session starts ==============================
platform darwin -- Python 3.7.3, pytest-5.1.1, py-1.8.0, pluggy-0.12.0
rootdir: sample_tdd
collected 1 item                                                               

sample_tdd/tests/test_hogehoge.py .                                      [100%]

============================== 1 passed in 0.02s ===============================

まとめ

pytest、pytest-watchを使ってのPythonによるテスト駆動開発を実施するための前準備として環境構築を行い、簡単なサンプルプログラムを実行し、正しくテストが実施されることを確認しました。今回記載したpytestの仕様上の規則等についても今後随時追加していきたいと思います。

階層型ディレクトリ構造の場合 (2019/9/26追記)

階層型ディレクトリ構造の場合(以下のソース例)のような場合のモジュールのインポートの方法を追記します。

  • 階層型ディレクトリ構造のソースの例
sample_source/
├ calculator
│   ├ __init__.py
│   ├ calculator.py
│   └ modules
│       ├ __init__.py
│       ├ add.py
│       ├ division.py
│       ├ multiple.py
│       └ subordinate.py
├ conftest.py
└ tests
    └ calculator
        ├ test_add.py
        ├ test_calculator.py
        ├ test_division.py
        ├ test_multiple.py
        └ test_subordinate.py

test_calculator.pyからcalculator.pyを読み込む場合

from calculator import Calculator

test_add.pyからadd.pyを読み込む場合

from calculator.modules import Add

テストが正しく実行されています。

========================= test session starts =========================
platform linux -- Python 3.7.4, pytest-5.1.1, py-1.8.0, pluggy-0.12.0  
rootdir: sample_source
collected 4 items                                                      

tests/calculator/test_add.py ..                                 [ 50%] 
tests/calculator/test_calculator.py ..                          [100%] 

========================== 4 passed in 0.70s ==========================

Reference

17
19
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
19