web-dev-qa-db-ja.com

Django manage.py:コマンドライン引数を渡すことは可能ですか(単体テスト用)

特に単体テストのために、コマンドライン引数をDjangoのmanage.pyスクリプトに渡すことは可能ですか?つまり、私が次のようなことをした場合

manage.py test myapp -a do_this

ユニットテストのsetUp関数で値do_thisを受け取ることはできますか?

P.S。 @Martinは、テストでコマンドライン引数を使用する理由を尋ねました。

  • 一部の大規模なテストには時間がかかり、すべてのコミットの前に実行する必要はありません。オプションにしたい。

  • 私のテストケースによって出力される時折のデバッグメッセージはオプションである必要があります

  • 時々、私はテストを狂わせて、データのより多くの順列を試してみたいだけです。

上記のすべては、コマンドラインオプションで非常に便利です。たまに、テストははるかに広範囲または冗長になる可能性があります。そうでない場合は迅速になります。

22
user4150760

私は自分でこの問題に遭遇したばかりで、コマンドラインで環境変数を設定することは避けたかったのです。環境変数は確かに機能しますが、どの変数が影響を与えるかを追跡することは困難であり、それらの1つを誤って入力したかどうかを通知するエラーメッセージはありません。

これを回避するために、argparseを使用してコマンドライン引数に追加のパラメーターを抽出しました。たとえば、私のmanage.pyファイルは次のようになります。

#!/usr/bin/env python
import os
import sys
import argparse


if __name__ == "__main__":
    os.environ.setdefault("Django_SETTINGS_MODULE", "project.settings")

    argv = sys.argv
    cmd = argv[1] if len(argv) > 1 else None
    if cmd in ['test']:  # limit the extra arguments to certain commands
        parser = argparse.ArgumentParser(add_help=False)
        parser.add_argument('--foo', default='bar')
        args, argv = parser.parse_known_args(argv)
        # We can save the argument as an environmental variable, in
        # which case it's to retrieve from within `project.settings`,
        os.environ['FOO'] = args.foo
        # or we can save the variable to settings directly if it
        # won't otherwise be overridden.
        from Django.conf import settings
        settings.foo = args.foo

    from Django.core.management import execute_from_command_line

    # parse_known_args strips the extra arguments from argv,
    # so we can safely pass it to Django.
    execute_from_command_line(argv)

argparseは、多くの機能を備えた本当に素晴らしいライブラリです。 Pythonドキュメントには良い チュートリアル があります。

9
clwainwright

プロジェクトで環境変数の回避策を使用しています(UNIXライクなシェルでのみ機能します)

berry$ myvar=myval ./manage.py test 

モジュールで、を使用してこの値を読み取ります

os.environ.get('myvar')
8
Berry Tsakala

Djangoでは、testrunnerクラスからカスタムコマンドラインオプションを追加できます。デフォルトのtestrunnerクラスのサブクラスを作成し、独自のオプションを追加してから、次のようにDjangoカスタムテストランナーを使用させることができます。

たとえば、Djangoプロジェクトディレクトリに、次の内容を含むtestrunner.pyを作成します。

_from Django.test.runner import DiscoverRunner

class TestRunner(DiscoverRunner):
    def __init__(self, option=None, **kwargs):
        super().__init__(**kwargs)

        print("Passed option: {}".format(option))

    @classmethod
    def add_arguments(cls, parser):
        DiscoverRunner.add_arguments(parser)

        parser.add_argument('-o', '--option', help='Example option')
_

これは、デフォルトのランナーから派生したテストランナーです(したがって、デフォルトと同じように機能します)。ただし、Djangoにコマンドラインオプションを追加するように指示します(add_arguments())。クラスメソッド)を作成し、コンストラクターでこの追加オプションの値を処理します。この新しいランナーで実行するには、次のように名前を渡します。

_./manage.py test --testrunner=testrunner.TestRunner -o foo
_

もちろん、コマンドラインで完全なインポート名を渡す限り、このクラスを他の場所に配置できます。

_--testrunner=foo_を使用する必要があることに注意してください。2つの別々の引数(_--testrunner foo_)を使用することはできません。その場合、追加の引数は機能しません。修正は保留中です: https://github.com/Django/django/pull/10307

この例では、オプション値を出力するだけですが、何らかの方法でそれをテストケースに渡す必要があります。単体テストのテストケースにオプションを渡す方法についての簡単な情報は見つかりませんでしたが、おそらくこれにはグローバル(モジュールレベル)変数またはクラス変数を使用できます(これはそれほど再入可能でエレガントではありませんが、簡単で動作します)。

4

manage.py test -a do_thisの代替方法として、特定の 設定ファイル を使用できます。

manage.py --settings=project.test_settings test

このファイルで必要なものを定義します。

# test_setting.py
SPECIFIC_OPTION = "test"

# tests.py
from Django.conf import settings
...
def setUp(self):
    if settings.SPECIFIC_OPTION:
        ....

本当に動的なオプションが必要な場合は、sys.argvtest_settings.pyを使用できますが、これは本当に汚いハックです。

4
erthalion