2012-08-27 12 views
7

私は自分のアプリケーションを単体テストしています。ほとんどのテストでは、特定の引数を持つ関数を呼び出して、戻り値と期待値の等しいかどうかを確認しています。Pythonユニットテスト内のオブジェクトの長い定義

一部のテストでは、期待される戻り値は比較的大きなオブジェクトです。そのうちの1つは、たとえば、5つの文字列をタプルのリストにマップする辞書です。そのオブジェクトを定義するには40〜50の反復コードが必要ですが、そのオブジェクトはテストしている関数の1つの期待値です。私のテスト関数の大部分は3-6行のコードで構成されているので、テスト関数内に期待される戻り値を定義するコードの40〜50行を持つことは望ましくありません。私はそのような状況のためのベストプラクティスを探しています。長い定義をテストの中に入れる正しい方法は何ですか?オブジェクトの

  • テストサンプル:がベースのいくつかの平等の主張を作るここで

    は、私は私がそれを見るように最善から最悪までランク付けされ、問題に対処するために考えていたアイデアですキーのサブセットにこれはコードの優雅さのためにテストの徹底を犠牲にします。別のモジュール内のオブジェクトを定義する

  • 平等の主張を行い、その後のテストで、モジュールをインポートして、別のの.pyファイル内のコードの長い40〜50行を書きます。これはテストを簡潔かつ簡潔にしますが、私はテストの補足として別のファイルを持つのが好きではありません。オブジェクト定義は結局テストの一部です。

  • テスト関数内でオブジェクトを定義する:これは私が避けたいと思う簡単な解決策です。私のテストはかなりシンプルで簡単ですが、そのオブジェクトの長い定義は適合しません。

多分私はきれいなコードに夢中ですが、私は上記の解決策のどれも好きではありません。私が考えていない別の一般的な練習はありますか?

+5

私の個人的な意見では、*テストルーチン*をテストデータ*から分離します。これにより、テストケースの再利用性が大幅に向上します。私は通常、別のテストモジュールで共通の基本クラスを定義します。私の実際のテストモジュールでは、抽象的なテストケースと実際のデータを特定のケースでリンクします。データは、インラインで配置することも、ファイルから読み込むこともできます。これはあなたに理にかなっていますか? – Constantinius

+0

私はdjangoのソースコードで使用されているテストを見ました。小さなオブジェクトはテスト関数内で定義されます。大きなオブジェクトは専用のファイルにあります(例:https://github.com/django/django/blob/master/tests/modeltests/aggregation/fixtures/aggregation.json) – Pramod

+0

個人的には、別のモジュールで定義する2番目のオプション) 一番です; Constantiniusがすでに述べたように、テストデータを実際のテストルーチンから分離するという追加の利点があります。最初の選択肢は私の本では最悪です。私はそれを見て、エレガントなコードは、機能の損失を犠牲にして来るべきではありません – Moritz

答えて

3

私は、テストコードのテストデータの分離を使用することをお勧めします。このため、私は通常、テストしたいメソッドを含む抽象基本クラスを作成し、そのメソッドをデータに結びつけるいくつかの特定のテストケースクラスを作成します。 (私は、Djangoフレームワーク、私はtestbase.pyに入れるので、すべての抽象テストクラスを使用します):

testbase.py

class TestSomeFeature(unittest.TestCase): 
    test_data_A = ... 

    def test_A(self): 
     ... #perform test 

、今test.pyで実装

class TestSomeFeatureWithDataXY(testbase.TestSomeFeature): 
    test_data_A = XY 

テストデータは、JSONファイルなどの外部化することもできます。

class TestSomeFeatureWithDataXYZ(testbase.TestSomeFeature): 
    @property 
    def test_data_A(self): 
     return json.load("data/XYZ.json") 

私はポイントを十分に明確にしたいと思う。あなたの場合、私はデータファイルの使用を強く選択します。 Djangoは、テストを実行する前にデータベースにロードされるテストフィクスチャを使って、これをすぐにサポートします。

+0

* code *と* data *は2つの異なるモジュールにある必要はないことに注意してください。あらかじめ計算された引数を属性として持つ 'TestData'クラスを定義し、これらの属性をテスト関数/クラスから呼び出すだけで十分です。 –

+0

@PierreGM:確かに、彼らは分離する必要はありませんが、私のポイントは実際にあなたのテストケースを構成し整理することです。小規模なプロジェクトの場合は、これはまったく必要ではありません。しかし、文字通り何百ものテストケースが浮かんでいるケースがあり、インラインのデータステートメント(またはモジュールの最上位に位置していても)は、冗長と戦うのを助けていません。 – Constantinius

+0

私達はすべて同意する、私達はすべての試みをしなければなりません。 –

1

実際にテストする内容によって異なります。

辞書に特定の値を持つ特定のキーが含まれているかどうかをテストする場合は、各キーを確認するためのアサーションを個別に提案します。この方法では、辞書が拡張され、テストの失敗が問題を明確に識別しなければなりません(50行の長い辞書の1つが2番目の50行の辞書と正確には一致しないというエラーメッセージが表示されます) 。

辞書に特定のキーだけが含まれていることを確認したい場合は、単一のアサーションが適切な場合があります。比較しているオブジェクトを最も明確な場所に定義します。それを別のファイルに定義すると(Constantiniusの答えが示唆するように)物事をより読みやすくします。

どちらの場合でも、ガイドラインの原則は気になる行動のみをテストすることです。気にしない振る舞いをテストすると、リファクタリング時に役立つよりも妨げになるかもしれません。

関連する問題