web-dev-qa-db-ja.com

予想されるデータを格納する場所

テスト関数パラメータを渡し、出力が期待される出力と一致することを確認する必要があります。

関数の応答がテスト関数内で定義できる小さな配列または1行の文字列である場合は簡単ですが、テストした関数が巨大になる可能性のある構成ファイルを変更するとします。または、明示的に定義すると、結果の配列は4行になります。テストをクリーンで維持しやすいように、どこに保管しますか?

今のところそれが文字列の場合は、_.py_テストの近くにファイルを置き、テスト内でopen()を実行します。

_def test_if_it_works():
    with open('expected_asnwer_from_some_function.txt') as res_file:
        expected_data = res_file.read()
    input_data = ... # Maybe loaded from a file as well
    assert expected_data == if_it_works(input_data)
_

このファイルを最新の状態に維持する問題など、このようなアプローチには多くの問題があります。それも悪く見えます。私はおそらくこれをフィクスチャに移動することでより良いものにすることができます:

_@pytest.fixture
def expected_data()
    with open('expected_asnwer_from_some_function.txt') as res_file:
        expected_data = res_file.read()
    return expected_data

@pytest.fixture
def input_data()
    return '1,2,3,4'

def test_if_it_works(input_data, expected_data):
    assert expected_data == if_it_works(input_data)
_

それは問題を別の場所に移動するだけで、通常、入力が空の場合、単一のアイテムまたは複数のアイテムで入力が機能するかどうかをテストする必要があるため、3つのケースすべてまたは複数のフィクスチャを含む1つの大きなフィクスチャを作成する必要があります。最後に、コードはかなり厄介になります。

関数が複雑な辞書を入力として期待する場合、または同じ巨大なテストコードの辞書を返す場合は、醜くなります。

_ @pytest.fixture
 def input_data():
     # It's just an example
     return {['one_value': 3, 'one_value': 3, 'one_value': 3,
     'anotherky': 3, 'somedata': 'somestring'], 
      ['login': 3, 'ip_address': 32, 'value': 53, 
      'one_value': 3], ['one_vae': 3, 'password': 13, 'lue': 3]}
_

このようなフィクスチャを使用してテストを読み取り、最新の状態に保つことは非常に困難です。

更新

しばらく検索したところ、大きな設定ファイルの代わりに大きなHTML応答があった場合、問題の一部を解決するライブラリが見つかりました。 betamax です。

使いやすくするために、フィクスチャを作成しました。

_from betamax import Betamax

@pytest.fixture
def session(request):
    session = requests.Session()
    recorder = Betamax(session)
    recorder.use_cassette(os.path.join(os.path.dirname(__file__), 'fixtures', request.function.__name__)
    recorder.start()
    request.addfinalizer(recorder.stop)
    return session
_

したがって、私のテストではsessionフィクスチャを使用するだけで、実行するすべてのリクエストは自動的に_fixtures/test_name.json_ファイルにシリアル化されるため、実際のHTTPリクエストライブラリのロードではなく、次にテストを実行するときにファイルシステムから:

_def test_if_response_is_ok(session):
   r = session.get("http://google.com")
_

これらのフィクスチャを最新の状態に保つには、fixturesフォルダーをクリーンアップしてテストを再実行する必要があるだけなので、非常に便利です。

27
Glueon

予想されるファイルに対して構成ファイルをテストしなければならないという、同様の問題が1回ありました。それが私がそれを修正した方法です:

  1. テストモジュールと同じ名前で同じ場所にフォルダーを作成します。予想されるすべてのファイルをそのフォルダーに入れます。

    test_foo/
        expected_config_1.ini
        expected_config_2.ini
    test_foo.py
    
  2. このフォルダの内容を一時ファイルに移動するためのフィクスチャを作成します。この件ではtmpdirフィクスチャを使用しました。

    from __future__ import unicode_literals
    from distutils import dir_util
    from pytest import fixture
    import os
    
    
    @fixture
    def datadir(tmpdir, request):
        '''
        Fixture responsible for searching a folder with the same name of test
        module and, if available, moving all contents to a temporary directory so
        tests can use them freely.
        '''
        filename = request.module.__file__
        test_dir, _ = os.path.splitext(filename)
    
        if os.path.isdir(test_dir):
            dir_util.copy_tree(test_dir, bytes(tmpdir))
    
        return tmpdir
    
  3. 新しいフィクスチャを使用します。

    def test_foo(datadir):
        expected_config_1 = datadir.join('expected_config_1.ini')
        expected_config_2 = datadir.join('expected_config_2.ini')
    

覚えておいてください:datadirtmpdirフィクスチャと同じですが、テストモジュールという名前の付いたフォルダーに配置された期待されるファイルを操作する機能があります。

31
Fabio Menegazzo

テスト数が少ない場合は、データを文字列リテラルとして含めないでください。

expected_data = """
Your data here...
"""

数が少ない場合、または予想されるデータが本当に長い場合は、フィクスチャを使用することには意味があると思います。

ただし、数が多い場合は、おそらく別のソリューションの方が適しています。実際、1つのプロジェクトで100を超える入力ファイルと予想出力ファイルがあります。したがって、私は自分のテストフレームワークを(多かれ少なかれ)構築しました。私はNoseを使用しましたが、PyTestも同様に機能しました。テストファイルのディレクトリをたどるテストジェネレータを作成しました。各入力ファイルについて、実際の出力と期待される出力を比較するテストが生成されました(PyTestはそれを parametrizing と呼んでいます)。次に、他のユーザーが使用できるようにフレームワークを文書化しました。テストを確認および/または編集するには、入力および/または予期される出力ファイルを編集するだけで、pythonテストファイルを確認する必要はありません。異なる入力ファイルに異なるオプションを有効にするには定義すると、各ディレクトリのYAML構成ファイルも作成しました(JSONも依存関係を維持するために機能します)YAMLデータは、各キーが入力ファイルの名前で、値がキーワードの辞書である辞書で構成されていますこれは、入力ファイルとともにテストされる関数に渡されます。興味がある場合は、ここに ソースコードdocumentation を示します。最近、オプションをUnittestsとして定義 here (組み込みのunittest libのみが必要です)が好きかどうかはわかりません。

3
Waylan

設定ファイルの内容全体を本当にテストする必要があるかどうか考えてください。

いくつかの値または部分文字列のみをチェックする必要がある場合は、その構成に必要なテンプレートを準備します。テストされた場所は、いくつかの特別な構文で「変数」としてマークされます。次に、テンプレート内の変数の値の予想される個別のリストを準備します。この予期されるリストは、個別のファイルとして、または直接ソースコードに保存できます。

テンプレートの例:

ALLOWED_HOSTS = ['{Host}']
DEBUG = {debug}
DEFAULT_FROM_EMAIL = '{email}'

ここでは、テンプレート変数は中括弧内に配置されています。

期待値は次のようになります。

Host = www.example.com
debug = False
email = [email protected]

または単純なコンマ区切りのリストとして:

www.example.com, False, [email protected]

次に、テストコードは、変数を期待される値で置き換えることにより、テンプレートから期待されるファイルを生成できます。そして、期待されるファイルが実際のファイルと比較されます。

テンプレートと期待値を別々に維持することには、同じテンプレートを使用して多くのテストデータセットを保持できるという利点があります。

変数のみのテスト

さらに良い方法は、構成生成メソッドが構成ファイルに必要な値のみを生成することです。これらの値は、別の方法でテンプレートに簡単に挿入できます。しかし、利点は、テストコードがすべての構成変数を個別に、明確な方法で直接比較できることです。

テンプレート

テンプレート内の変数を必要な値に置き換えるのは簡単ですが、1行でのみ実行できる準備ができたテンプレートライブラリがあります。以下に例をいくつか示します。 DjangoJinjaMako

pytest-datafiles は非常に役立つと思います。残念ながら、あまりメンテナンスされていないようです。当面は、問題なく動作しています。

これはドキュメントから取られた簡単な例です:

import os
import pytest

@pytest.mark.datafiles('/opt/big_files/film1.mp4')
def test_fast_forward(datafiles):
    path = str(datafiles)  # Convert from py.path object to path (str)
    assert len(os.listdir(path)) == 1
    assert os.path.isfile(os.path.join(path, 'film1.mp4'))
    #assert some_operation(os.path.join(path, 'film1.mp4')) == expected_result

    # Using py.path syntax
    assert len(datafiles.listdir()) == 1
    assert (datafiles / 'film1.mp4').check(file=1)
0
Dror