web-dev-qa-db-ja.com

典型的なテストディレクトリ構造でunittestを実行する

単純なPythonモジュールでも非常に一般的なディレクトリ構造は、単体テストを独自のtestディレクトリに分割することです。

new_project/
    antigravity/
        antigravity.py
    test/
        test_antigravity.py
    setup.py
    etc.

例えば、こちら のPythonプロジェクトの手引き をご覧ください。

私の質問は単純です実際にテストを実行する通常の方法は何ですか?これは私以外のすべての人にとって明らかなことですが、テストディレクトリからpython test_antigravity.pyを実行することはできません。モジュールがパス上にないため、import antigravityは失敗します。

PYTHONPATHや他の検索パスに関連するトリックを変更することはできますが、それが最も簡単な方法であるとは思えません。開発者であれば問題ありません。通過。

もう1つの方法は、テストファイルを他のディレクトリにコピーすることですが、少し愚かに見え、最初は別のディレクトリに置くという点を見逃しています。

それで、あなたがちょうど私の新しいプロジェクトにソースをダウンロードしたならば、どのようにあなたは単体テストを実行するでしょうか?私は私のユーザーに私に言うことができる答えが欲しいです: "ユニットテストを実行するにはXをしてください。"

595
Major Major

私の考えでは、最良の解決策は、ディレクトリをsys.pathに追加するunittestコマンドラインインタフェース を使用することです。 TestLoaderクラス).

たとえば、次のようなディレクトリ構造の場合

new_project
├── antigravity.py
└── test_antigravity.py

あなただけ実行することができます:

$ cd new_project
$ python -m unittest test_antigravity

あなたのようなディレクトリ構造の場合:

new_project
├── antigravity
│   ├── __init__.py         # make it a package
│   └── antigravity.py
└── test
    ├── __init__.py         # also make test a package
    └── test_antigravity.py

testパッケージ内のテストモジュールでは、antigravityパッケージとそのモジュールを通常どおりインポートできます。

# import the package
import antigravity

# import the antigravity module
from antigravity import antigravity

# or an object inside the antigravity module
from antigravity.antigravity import my_object

単一のテストモジュールを実行する:

単一のテストモジュールを実行するには、この場合test_antigravity.py

$ cd new_project
$ python -m unittest test.test_antigravity

テストモジュールをインポートするのと同じ方法で参照してください。

単一のテストケースまたはテストメソッドを実行する:

また、単一のTestCaseまたは単一のテストメソッドを実行することもできます。

$ python -m unittest test.test_antigravity.GravityTestCase
$ python -m unittest test.test_antigravity.GravityTestCase.test_method

すべてのテストを実行中:

また、 テストディスカバリー を使用して、すべてのテストを検出して実行することもできます。それらはtest*.pyという名前のモジュールまたはパッケージでなければなりません(-p, --patternで変更できます)フラグ):

$ cd new_project
$ python -m unittest discover

これはtestパッケージ内のすべてのtest*.pyモジュールを実行します。

555
Pierre

ユーザーにとって最も簡単な解決策は、必要に応じて一時的にsys.pathにルートプロジェクトディレクトリを追加するなど、必要なテスト環境をブートストラップする実行可能スクリプト(runtests.pyなど)を提供することです。これはユーザーが環境変数を設定することを要求しません、ブートストラップスクリプトでこのようなものはうまく働きます:

import sys, os

sys.path.insert(0, os.path.dirname(__file__))

それからあなたのユーザへのあなたの指示は "python runtests.py"と同じくらい簡単になることができます。

もちろん、必要なパスがos.path.dirname(__file__)であれば、それをsys.pathに追加する必要はまったくありません。 Pythonは常に現在実行中のスクリプトのディレクトリをsys.pathの先頭に置くので、ディレクトリ構造によっては、正しい場所にruntests.pyを配置するだけでよい場合があります。

また、Python 2.7の unittestモジュールには+ (Python 2.6以前では unittest2 としてバックポートされています)が になりました。 ]テストディスカバリ ビルトインなので、自動テストディスカバリをしたいのであれば、もはや鼻は必要ありません。ユーザの指示は "python -m unittest discover"と同じくらい簡単です。

46
Carl Meyer

私は通常、プロジェクトディレクトリ(ソースディレクトリとtestの両方に共通のもの)に "テストの実行"スクリプトを作成して、 "すべてのテスト"スイートをロードします。これは通常定型コードなので、プロジェクトごとに再利用できます。

run_tests.py:

import unittest
import test.all_tests
testSuite = test.all_tests.create_test_suite()
text_runner = unittest.TextTestRunner().run(testSuite)

test/all_tests.py(from すべてのPythonユニットテストをディレクトリで実行するにはどうすればよいですか?

import glob
import unittest

def create_test_suite():
    test_file_strings = glob.glob('test/test_*.py')
    module_strings = ['test.'+str[5:len(str)-3] for str in test_file_strings]
    suites = [unittest.defaultTestLoader.loadTestsFromName(name) \
              for name in module_strings]
    testSuite = unittest.TestSuite(suites)
    return testSuite

この設定では、テストモジュールの中で本当にinclude antigravityだけできます。欠点は、特定のテストを実行するためのサポートコードがもっと必要になることです...私は毎回それらをすべて実行します。

20
stw_dev

あなたがリンクした記事から:

Test_modulename.pyファイルを作成し、そこにあなたのunittestテストを入れてください。テストモジュールはコードとは別のディレクトリにあるため、それらを実行するには、モジュールの親ディレクトリをPYTHONPATHに追加する必要があります。

$ cd /path/to/googlemaps

$ export PYTHONPATH=$PYTHONPATH:/path/to/googlemaps/googlemaps

$ python test/test_googlemaps.py

最後に、Python用のもう1つの一般的な単体テストフレームワークがあります(それはとても重要です)。 noseは、組み込みのunittestフレームワークを単純化し拡張するのに役立ちます(例えば、テストコードを自動的に見つけてPYTHONPATHを自動的に設定することができます)が、標準のPython配布には含まれていません。

おそらく、あなたは を見てみるべきでしょうか?

18
Mark Byers

"python setup.py Develop"を実行すると、パッケージはパスに入ります。しかし、あなたはあなたのシステムのpythonインストールに感染する可能性があるのでそれをしたくないかもしれません、それは virtualenvbuildout のようなツールが存在する理由です。

8
Tom Willis

私は別のユニットテストフォルダで、同じ問題を抱えていました。前述の提案から、私は絶対ソースパスsys.pathに追加します。

次の解決策の利点は、最初にtestディレクトリに変更することなくファイルtest/test_yourmodule.pyを実行できることです。

import sys, os
testdir = os.path.dirname(__file__)
srcdir = '../antigravity'
sys.path.insert(0, os.path.abspath(os.path.join(testdir, srcdir)))

import antigravity
import unittest
8
andpei

VS Codeを使用していて、テストがプロジェクトと同じレベルにある場合は、コードの実行とデバッグはそのままでは機能しません。できることは、launch.jsonファイルを変更することです。

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Python",
            "type": "python",
            "request": "launch",
            "stopOnEntry": false,
            "pythonPath": "${config:python.pythonPath}",
            "program": "${file}",
            "cwd": "${workspaceRoot}",
            "env": {},
            "envFile": "${workspaceRoot}/.env",
            "debugOptions": [
                "WaitOnAbnormalExit",
                "WaitOnNormalExit",
                "RedirectOutput"
            ]
        }    
    ]
}

ここでのキーラインはenvFileです。

"envFile": "${workspaceRoot}/.env",

プロジェクトのルートに.envファイルを追加します。

.envファイルの中に、プロジェクトのルートへのパスを追加します。これは一時的に追加されます

PYTHONPATH = C:\あなたの\ PYTHON\PROJECT\ROOT_DIRECTORY

プロジェクトへのパスを入力すると、VS Codeのデバッグ単体テストを使用できるようになります。

5
Vlad Bezden

Python unittestモジュールのための解決策/例

以下のプロジェクト構造を考えます。

ProjectName
 ├── project_name
 |    ├── models
 |    |    └── thing_1.py
 |    └── __main__.py
 └── test
      ├── models
      |    └── test_thing_1.py
      └── __main__.py

python project_nameを呼び出すProjectName/project_name/__main__.pyを使用して、ルートディレクトリからプロジェクトを実行できます。


python testを使用してテストを実行し、効果的にProjectName/test/__main__.pyを実行するには、次の手順を実行する必要があります。

1)test/modelsファイルを追加して、__init__.pyディレクトリをパッケージにします。これにより、サブディレクトリ内のテストケースは、親のtestディレクトリからアクセス可能になります。

# ProjectName/test/models/__init__.py

from .test_thing_1 import Thing1TestCase        

2)test/__main__.py内のシステムパスを、project_nameディレクトリを含むように変更します。

# ProjectName/test/__main__.py

import sys
import unittest

sys.path.append('../project_name')

loader = unittest.TestLoader()
testSuite = loader.discover('test')
testRunner = unittest.TextTestRunner(verbosity=2)
testRunner.run(testSuite)

これで、テストでproject_nameから正常にインポートできました。

# ProjectName/test/models/test_thing_1.py    

import unittest
from project_name.models import Thing1  # this doesn't work without 'sys.path.append' per step 2 above

class Thing1TestCase(unittest.TestCase):

    def test_thing_1_init(self):
        thing_id = 'ABC'
        thing1 = Thing1(thing_id)
        self.assertEqual(thing_id, thing.id)
5
Derek Soike

作業ディレクトリをインストール済みのPython環境の一部にするためにsetup.py developを使用してから、テストを実行します。

5
Ned Batchelder

選択したテストまたはすべてのテストを実行するラッパーを使用することができます。

例えば:

./run_tests antigravity/*.py

またはすべてのテストを再帰的に実行するには、 globbingtests/**/*.py)を使用します(shopt -s globstarで有効にします)。

ラッパーは基本的にargparseを使用して以下のように引数を解析できます。

parser = argparse.ArgumentParser()
parser.add_argument('files', nargs='*')

それからすべてのテストをロードします。

for filename in args.files:
    exec(open(filename).read())

それからそれらをテストスイートに追加します(inspectを使用)。

alltests = unittest.TestSuite()
for name, obj in inspect.getmembers(sys.modules[__name__]):
    if inspect.isclass(obj) and name.startswith("FooTest"):
        alltests.addTest(unittest.makeSuite(obj))

そしてそれらを実行します。

result = unittest.TextTestRunner(verbosity=2).run(alltests)

詳細は this exampleを確認してください。

すべてのPython単体テストをディレクトリで実行する方法?

4
kenorb

あなたの "src"ディレクトリからunittestのコマンドラインインターフェイスを実行すれば、インポートは変更なしで正しく動作することに気づきました。

python -m unittest discover -s ../test

プロジェクトディレクトリのバッチファイルに入れたい場合は、次のようにします。

setlocal & cd src & python -m unittest discover -s ../test
4
Alan L

以下は私のプロジェクト構造です。

ProjectFolder:
 - project:
     - __init__.py
     - item.py
 - tests:
     - test_item.py

SetUp()メソッドでインポートした方が良いことがわかりました。

import unittest
import sys    

class ItemTest(unittest.TestCase):

    def setUp(self):
        sys.path.insert(0, "../project")
        from project import item
        # further setup using this import

    def test_item_props(self):
        # do my assertions

if __== "__main__":
    unittest.main()
3
rolika

実際にテストを実行する通常の方法は何ですか

私はPython 3.6.2を使っています

cd new_project

pytest test/test_antigravity.py

pytestをインストールするには:Sudo pip install pytest

パス変数を設定しなかったので、インポートが同じ「テスト」プロジェクト構造で失敗することはありません。

私はこのことをコメントアウトしました:if __== '__main__'はこのように:

test_antigravity.py

import antigravity

class TestAntigravity(unittest.TestCase):

    def test_something(self):

        # ... test stuff here


# if __== '__main__':
# 
#     if __package__ is None:
# 
#         import something
#         sys.path.append(path.dirname(path.dirname(path.abspath(__file__))))
#         from .. import antigravity
# 
#     else:
# 
#         from .. import antigravity
# 
#     unittest.main()
3
aliopi

Python 3+

@Pierreに追加する

このようにunittestディレクトリ構造を使う:

new_project
├── antigravity
│   ├── __init__.py         # make it a package
│   └── antigravity.py
└── test
    ├── __init__.py         # also make test a package
    └── test_antigravity.py

テストモジュールtest_antigravity.pyを実行するには

$ cd new_project
$ python -m unittest test.test_antigravity

あるいは単一のTestCase

$ python -m unittest test.test_antigravity.GravityTestCase

必須空でも__init__.pyを忘れないでください。

2
eusoubrasileiro

あなたは、なんらかのブードゥーがなければ、親ディレクトリからインポートすることはできません。これは少なくともPython 3.6で動作するもう一つの方法です。

まず、test/context.pyファイルに次の内容を入れます。

import sys
import os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))

次に、test/test_antigravity.pyファイルに次のインポートを入れます。

import unittest
try:
    import context
except ModuleNotFoundError:
    import test.context    
import antigravity

このtry-except節の理由は、

  • "python test_antigravity.py"で実行するとimport test.contextが失敗し、
  • new_projectディレクトリから "python -m unittest"を指定して実行すると、インポートコンテキストが失敗します。

このトリックで、彼らは両方とも働きます。

これで、testディレクトリ内のテストファイルをすべてall実行することができます。

$ pwd
/projects/new_project
$ python -m unittest

または個別のテストファイルを次のように実行します。

$ cd test
$ python test_antigravity

わかりました、test_antigravity.pyの中にcontext.pyの内容を入れるよりもきれいではありませんが、少しだけかもしれません。提案は大歓迎です。

2
tjk

このBASHスクリプトはファイルシステムのどこからでもpythonのunittestテストディレクトリを実行します。どんな作業ディレクトリにいても関係ありません。

これは./srcまたは./example作業ディレクトリにいるときに便利で、簡単な単体テストが必要です。

#!/bin/bash

this_program="$0"
dirname="`dirname $this_program`"
readlink="`readlink -e $dirname`"

python -m unittest discover -s "$readlink"/test -v

プロダクション中にあなたのパッケージ/メモリオーバーヘッドを負担するためにtest/__init__.pyファイルを必要としません。

1
Egbert S

テストディレクトリに複数のディレクトリがある場合は、各ディレクトリに__init__.pyファイルを追加する必要があります。

/home/johndoe/snakeoil
└── test
    ├── __init__.py        
    └── frontend
        └── __init__.py
        └── test_foo.py
    └── backend
        └── __init__.py
        └── test_bar.py

それからすべてのテストを一度に実行するには、次のように実行します。

python -m unittest discover -s /home/johndoe/snakeoil/test -t /home/johndoe/snakeoil

ソース:python -m unittest -h

  -s START, --start-directory START
                        Directory to start discovery ('.' default)
  -t TOP, --top-level-directory TOP
                        Top level directory of project (defaults to start
                        directory)
0
Qlimax

この方法により、コマンドラインからシステム変数をいじることなく、どこからでもテストスクリプトを実行できます。

これにより、メインプロジェクトフォルダーがpythonパスに追加されます。その場所は、現在の作業ディレクトリではなく、スクリプト自体に関連して検出されます。

import sys, os

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.realpath(__file__))))

すべてのテストスクリプトの先頭に追加します。これにより、システムパスにメインプロジェクトフォルダーが追加されるため、そこから機能するモジュールインポートはすべて機能するようになります。そして、どこからテストを実行するかは問題ではありません。

明らかに、project_path_hackファイルを変更して、メインプロジェクトフォルダーの場所に一致させることができます。

0
chasmani

あなたは本当にpipツールを使うべきです。

開発モードでパッケージをインストールするにはpip install -e .を使用してください。これはpytestによって推奨される非常に良い習慣です(彼らが従うべき2つのプロジェクトレイアウトを見つけることができるそこで彼らの 良い習慣ドキュメンテーション を見てください)。

0
squid

コマンドラインのみの解決策を探しているなら:

次のディレクトリ構造に基づいています(専用のソースディレクトリで一般化されています)。

new_project/
    src/
        antigravity.py
    test/
        test_antigravity.py

Windows:(new_project内)

$ set PYTHONPATH=%PYTHONPATH%;%cd%\src
$ python -m unittest discover -s test

これをバッチforループで使用したい場合は、この質問を参照してください。

Linux:(new_project内)

$ export PYTHONPATH=$PYTHONPATH:$(pwd)/src  [I think - please edit this answer if you are a Linux user and you know this]
$ python -m unittest discover -s test

この方法では、必要に応じてPYTHONPATHにディレクトリを追加することもできます。

0
pj.dewitte