web-dev-qa-db-ja.com

Django単体テストでモックを使用してセロリタスクにパッチを適用する

pythonモックライブラリを使用して、モデルがDjangoアプリに保存されたときに実行されるCeleryタスクにパッチを適用し、それが正しく呼び出されている。

基本的に、タスクは_myapp.tasks_内で定義され、次のようにmodels.py-fileの先頭にインポートされます。

_from .tasks import mytask_

...次に、save()を使用してモデル内のmytask.delay(foo, bar)で実行します。これまでのところ良い-実際にセレリッドなどを実行しているときにうまくいきます.

タスクを模倣する単体テストを作成し、正しい引数で呼び出され、実際にCeleryタスクを実行しようとしないことを確認するだけです。

したがって、テストファイルでは、標準のTestCase内に次のようなものが含まれています。

_from mock import patch # at the top of the file

# ...then later
def test_celery_task(self):
    with patch('myapp.models.mytask.delay') as mock_task:
        # ...create an instance of the model and save it etc
        self.assertTrue(mock_task.called)
_

...しかし、呼び出されることはありません/常にfalseです。私はさまざまな化身を試しました(代わりに_myapp.models.mytask_をパッチし、代わりに_mock_task.delay_が呼び出されたかどうかを確認しました)インポートパスが重要であることをモックドキュメントから収集しました。テスト中のモジュール内で見られるパス(正しく理解すれば_myapp.models.mytask.delay_ではなく_myapp.tasks.mytask.delay_になります)。

ここでどこがいけないのですか?セロリのタスクにパッチを適用する際に特定の問題はありますか?代わりに_celery.task_(mytaskのデコレータとして使用されます)にパッチを適用できますか?

34
Emil

あなたが抱えている問題は、これがセロリの仕事であるという事実とは無関係です。あなたはたまたま間違ったものにパッチを当てているだけです。 ;)

具体的には、「mytask」をインポートしているビューまたはその他のファイルを見つけ、そこにパッチを適用する必要があるため、関連する行は次のようになります。

with patch('myapp.myview.mytask.delay') as mock_task:

ここにはこれにいくつかの味があります:

http://www.voidspace.org.uk/python/mock/patch.html#where-to-patch

44
Thanos Diacakis

_@task_デコレータは、関数をTaskオブジェクトに置き換えます( documentation を参照)。タスク自体をモックすると、(やや魔法の)TaskオブジェクトがMagicMockに置き換わり、タスクはまったくスケジュールされません。代わりに、次のようにTaskオブジェクトのrun()メソッドをモックします。

_@override_settings(CELERY_ALWAYS_EAGER=True)
@patch('monitor.tasks.monitor_user.run')
def test_monitor_all(self, monitor_user):
    """
    Test monitor.all task
    """

    user = ApiUserFactory()
    tasks.monitor_all.delay()
    monitor_user.assert_called_once_with(user.key)
_
28