web-dev-qa-db-ja.com

クエリセットを受け取る関数をテストするために、Djangoクエリセットをモックする

Djangoプロジェクトにユーティリティ関数があります。クエリセットを受け取り、そこからデータを取得して結果を返します。この関数のテストをいくつか作成したいと思います。とにかくQuerySetを「モック」しますか?データベースにアクセスしないオブジェクトを作成したいのですが、使用する値のリスト(つまり、いくつかの偽の行)を提供すると、クエリセットのように機能します。 、そして誰かがit/filter/get/allなどでフィールドルックアップを実行できるようにします。

このようなものはすでに存在しますか?

33
Rory

もちろん、QuerySetをモックすることも、何でもモックすることができます。

オブジェクトを自分で作成し、必要なインターフェイスを指定して、必要なデータを返すようにすることができます。本質的に、モックは、テストの目的のために本物のように十分に機能する「テストダブル」を提供することに他なりません。

始めるためのローテクな方法は、オブジェクトを定義することです。

class MockQuerySet(object):
    pass

次に、これらの1つを作成し、テストに渡します。テストは失敗します。おそらくAttributeErrorで失敗します。これにより、MockQuerySetに何を実装する必要があるかがわかります。オブジェクトがテストに十分なリッチになるまで繰り返します。

16
Ned Batchelder

私は同じ問題を抱えています、そして何人かの素敵な人がQuerySetsをモックするためのライブラリを書いたようです、それは mock-Django と呼ばれ、あなたが必要とする特定のコードはここにあります https: //github.com/dcramer/mock-Django/blob/master/mock_Django/query.py モデルオブジェクト関数にパッチを適用して、何かを返すように設定したこれらのQuerySetMockオブジェクトの1つを返すことができると思います期待!

13
Ctrlspc

空のクエリセットの場合、単純にnonekeithhackbarthはすでに述べています として使用します。

ただし、値のリストを返すクエリセットをモックするには、モデルのマネージャーのspecモック を使用することをお勧めします。例として(Python 2.7スタイル- 外部モックライブラリを使用しました )、クエリセットをフィルタリングしてカウントする簡単なテストを次に示します。

from Django.test import TestCase
from mock import Mock

from .models import Example


def queryset_func(queryset, filter_value):
    """
    An example function to be tested
    """
    return queryset.filter(stuff=filter_value).count()


class TestQuerysetFunc(TestCase):

    def test_happy(self):
        """
        `queryset_func` filters provided queryset and counts result
        """
        m_queryset = Mock(spec=Example.objects)
        m_queryset.filter.return_value = m_queryset
        m_queryset.count.return_value = 97

        result = func_to_test(m_queryset, '__TEST_VALUE__')

        self.assertEqual(result, 97)
        m_queryset.filter.assert_called_once_with(stuff='__TEST_VALUE__')
        m_queryset.count.assert_called_once_with()

ただし、質問を満たすために、countreturn_valueを設定する代わりに、これをlistから返されるモデルインスタンスのallに簡単に調整できます。

連鎖は、モックされたクエリセットを返すようにfilterを設定することによって処理されることに注意してください。

m_queryset.filter.return_value = m_queryset

これは、テスト対象の関数で使用されるクエリセットメソッドに適用する必要があります。 excludeなど。

9
jamesc

これには Djangoの.none()関数 を使用します。

例えば:

class Location(models.Model):
  name = models.CharField(max_length=100)
mock_locations = Location.objects.none()

これは、Django独自の内部テストケースで頻繁に使用される方法です。コード内のコメントに基づく

Calling none() will create a queryset that never returns any objects and no
+query will be executed when accessing the results. A qs.none() queryset
+is an instance of ``EmptyQuerySet``.
4
keithhackbarth

FactoryBoyを調べましたか? https://factoryboy.readthedocs.io/en/latest/orms.html これはDjango ormをサポートするフィクスチャ交換ツールです-工場は基本的にormを生成します-オブジェクトのように(メモリ内またはテストデータベース内)。

始めるための素晴らしい記事は次のとおりです: https://www.caktusgroup.com/blog/2013/07/17/factory-boy-alternative-Django-testing-fixtures/

2
Liz

Django_mock_queries library これにより、データベースアクセスをモックアウトし、フィルタリングなどのDjangoクエリセット機能の一部を引き続き使用できます。

完全な開示:私はプロジェクトにいくつかの機能を提供しました。

1
Don Kirkby

あなたはこのようにモックすることができます:

@patch('Django.db.models.query.QuerySet')
def test_returning_distinct_records_for_city(self, mock_qs):
    self.assertTrue(mock_qs.called)
0
Julio Marins