Djangoでのユニットテスト


12

私は大規模なDjangoプロジェクトの効果的な単体テストを書くのに本当に苦労しています。私はかなり良いテストカバレッジを持っていますが、私が書いてきたテストは間違いなく統合/受け入れテストであり、ユニットテストではないことに気付きました。これをできるだけ早く修正したいと思います。

これが私の問題です。私のスキーマは深いリレーショナルであり、非常に時間指向であり、モデルオブジェクトに高い内部結合と多くの状態を与えます。私のモデルメソッドの多くは、時間間隔に基づいてクエリを実行auto_now_addし、タイムスタンプ付きのフィールドで多くのことを行っています。したがって、たとえば次のようなメソッドを使用します。

def summary(self, startTime=None, endTime=None):
    # ... logic to assign a proper start and end time 
    # if none was provided, probably using datetime.now()

    objects = self.related_model_set.manager_method.filter(...)

    return sum(object.key_method(startTime, endTime) for object in objects)

このようなものをテストする方法はありますか?

ここに私がここにいます。ユニットテストの目的には、その引数にいくつかの模擬動作与える必要がありますが、正しい結果を生成するために正しくフィルタリング/集計されていますか?by key_methodsummary

datetime.now()のモックは簡単ですが、残りの動作をどのようにモックできますか?

  • フィクスチャを使用することもできますが、データを構築するためにフィクスチャを使用することの長所と短所を耳にしました(保守性が悪いことは、私にとっては当たり前のことです)。
  • ORMを使用してデータを設定することもできますが、関連するオブジェクトも作成する必要があるため、制限される可能性があります。また、ORMでは、auto_now_addフィールドを手動で変更することはできません。
  • ORMのモックは別のオプションですが、深くネストされたORMメソッドをモックするのが難しいだけでなく、ORMコードのロジックがテストからモックされるため、モックはテストをテストの内部と依存関係に本当に依存させるようですテスト対象機能。

クラックするのが最も難しいのは、このような関数であると思われます。これらの関数は、モデルのいくつかの層と下位レベルの関数にあり、これらの関数は非常に複雑ではないかもしれませんが、時間に非常に依存しています。私の全体的な問題は、どのようにスライスしたように見えても、テストはテストしている機能よりもはるかに複雑に見えることです。


最初に単体テストを作成する必要があります。これにより、実際の量産コードを作成する前に、設計のテスト容易性の問題を特定できます。
Chedy2149

2
これは役立ちますが、本質的にステートフルでORMが重いアプリケーションをどのように最適にテストするかという問題に実際には対処していません。
acjay

永続化レイヤーを抽象化する必要があります
Chedy2149

1
仮説的には素晴らしいように聞こえますが、プロジェクトのメンテナンスに関しては、ビジネスロジックと非常によく文書化されたDjango ORMの間にカスタムメイドの永続化レイヤーを挿入するのは簡単なコストだと思います。突然、クラスには、時間の経過とともにリファクタリングする必要のある多数の小さな中間メソッドが詰め込まれます。しかし、おそらくこれはテスト容易性が重要な場所で正当化されます。
acjay

チェックアウト:vimeo.com/43612849およびこれ:vimeo.com/15007792
Chedy2149

回答:


6

先に進んで、これまでに思いついたことの答えを登録します。

私の仮説は、深い結合と状態のある関数の場合、現実には、外部のコンテキストを制御するために単純に多くの線を引くということです。

以下は、標準のMockライブラリに依存しているおおよそのテストケースです。

  1. イベントのシーケンスを設定するために標準ORMを使用します
  2. 独自の開始を作成し、設計の固定タイムラインに合わせdatetimeauto_now_add時間を覆します。ORMはこれを許可していないと思っていましたが、うまくいきます。
  3. テスト対象の関数が使用することを確認して、その関数だけにfrom datetime import datetimeパッチを適用できるようにdatetime.now()します(datetimeクラス全体をモックすると、ORMが適合します)。
  4. object.key_method()引数に依存するシンプルだが明確に定義された機能を使用して、の独自の置換を作成します。そうしないと、テスト対象の関数のロジックが機能しているかどうかがわからない可能性があるため、引数に依存するようにします。私の場合、それは単に間の秒数を返すstartTimeと、をendTime。ラムダでラップobject.key_method()し、new_callablekwargの使用に直接パッチを当てることでパッチを当てpatchます。
  5. 最後に、さまざまなsummary引数でのさまざまな呼び出しで一連のアサートを実行して、モックの所定の動作を考慮した予想される手計算結果との同等性をチェックしますkey_method

言うまでもなく、これは関数自体よりもかなり長く複雑です。DBに依存し、実際には単体テストのようには感じません。しかし、それは関数の内部からもかなり切り離されています-そのシグネチャと依存関係だけです。ですから、実際には単体テストかもしれません。

私のアプリでは、この関数は極めて重要であり、パフォーマンスを最適化するためにリファクタリングの対象となります。ですから、複雑さにもかかわらず、トラブルはそれだけの価値があると思います。しかし、私はまだこれにアプローチする方法についてのより良いアイデアを受け入れています。よりテスト駆動型の開発スタイルへの私の長い旅のすべて...

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.