Python
Django

djangoでDBを使ったテストを書く時、 `setUpTestData()` を使うと早く出来る場合がある

More than 1 year has passed since last update.

djangoでDBを使ったテストを書く時、 setUpTestData() を使うと早く出来る場合がある

djangoでDBを使ったテストを書く時、 setUpTestData() を使うと早く出来る場合がある。

setUpTestData()

setUpTestData() は、通常、setUp() で各メソッド毎に行われるモデルの初期化などをクラス単位で行おうというものです。(内部の実装を覗いてみると少しおもしろいかもしれません。)

もちろん、クラス単位で初期化されるということは、一度しか呼ばれないということなので、状態の更新などが行われるものについては注意が必要です。

例えば、テキトウに、django.contrib.auth.models.User の検索のテストを書くとします。
(特に User を選んだ理由は無いです。単にモデルの定義がめんどうだっただけです。)

毎回 setUp() を呼び出しているもの

以下のようなテストケースでは、Userのモデルの生成がテストメソッドの数だけ行われます。遅い。

class Tests(TestCase):
    def setUp(self):
        from django.contrib.auth.models import User
        for i in range(110):
            User.objects.create_superuser("admin{}".format(i), "myemail{}@example.com".format(i), '')

    def test_query(self):
        from django.contrib.auth.models import User
        with self.assertNumQueries(1):
            actual = User.objects.filter(username__startswith="admin1").count()
            self.assertEqual(actual, 11)

    def test_query1(self):
        from django.contrib.auth.models import User
        with self.assertNumQueries(1):
            actual = User.objects.filter(username__startswith="admin1").count()
            self.assertEqual(actual, 11)

    def test_query2(self):
        from django.contrib.auth.models import User
        with self.assertNumQueries(1):
            actual = User.objects.filter(username__startswith="admin1").count()
            self.assertEqual(actual, 11)

setUpTestData() を使ったもの

class Tests(TestCase):
    @classmethod
    def setUpTestData(cls):
        from django.contrib.auth.models import User
        for i in range(100):
            User.objects.create_superuser("admin{}".format(i), "myemail{}@example.com".format(i), '')

    def test_query(self):
        from django.contrib.auth.models import User
        with self.assertNumQueries(1):
            actual = User.objects.filter(username__startswith="admin1").count()
            self.assertEqual(actual, 11)

    def test_query1(self):
        from django.contrib.auth.models import User
        with self.assertNumQueries(1):
            actual = User.objects.filter(username__startswith="admin1").count()
            self.assertEqual(actual, 11)

    def test_query2(self):
        from django.contrib.auth.models import User
        with self.assertNumQueries(1):
            actual = User.objects.filter(username__startswith="admin1").count()
            self.assertEqual(actual, 11)

実行時間の比較

実行時間の比較をしてみます。テストメソッドの数だけ setUp() が呼ばれれば呼ばれる程遅くなるというだけで結果の比率などにあまり意味は無いです。3つテストメソッドがあるので、setUp() でデータを生成している方がおよそ3倍時間がかかります。

setUp setUpTestData
Userの数 100 100
テストメソッドの数 3 3
経過時間 11.7s 3.6s

実際に試して見たければ以下のgistで確認できます。

https://gist.github.com/podhmo/ffc6f96e4688dfb53810f4e4d6ba4d92