しばらくの間、私の単体テストには予想よりも長い時間がかかっていました。テストが実行を開始する前に遅延が発生するため、あまり成功せずに数回デバッグしようとしました。これは、テスト駆動型開発に近いリモートで何かを実行する能力に影響を与えているので(おそらく私の期待が高すぎる)、これを完全に修正できるかどうかを確認したいと思います。
テストを実行すると、テストの開始から実際の開始までに70〜80秒の遅延があります。たとえば、小さなモジュール(time python manage.py test myapp
を使用)のテストを実行すると、
<... bunch of unimportant print messages I print from my settings>
Creating test database for alias 'default'...
......
----------------------------------------------------------------
Ran 6 tests in 2.161s
OK
Destroying test database for alias 'default'...
real 1m21.612s
user 1m17.170s
sys 0m1.400s
1m:21の約1m18は
Creating test database for alias 'default'...
そしてその
.......
ライン。つまり、テストには3秒未満かかりますが、データベースの初期化には1:18分かかっているようです。
約30個のアプリがあり、そのほとんどに1〜3個のデータベースモデルが含まれているため、プロジェクトのサイズがわかります。ユニットテストにSQLiteを使用し、提案された改善のいくつかを実装しました。設定ファイル全体を投稿することはできませんが、必要な情報を追加できれば幸いです。
私はランナーを使用します
from Django.test.runner import DiscoverRunner
from Django.conf import settings
class ExcludeAppsTestSuiteRunner(DiscoverRunner):
"""Override the default Django 'test' command, exclude from testing
apps which we know will fail."""
def run_tests(self, test_labels, extra_tests=None, **kwargs):
if not test_labels:
# No appnames specified on the command line, so we run all
# tests, but remove those which we know are troublesome.
test_labels = (
'app1',
'app2',
....
)
print ('Testing: ' + str(test_labels))
return super(ExcludeAppsTestSuiteRunner, self).run_tests(
test_labels, extra_tests, **kwargs)
そして私の設定では:
TEST_RUNNER = 'config.test_runner.ExcludeAppsTestSuiteRunner'
Django-nose
をDjango-nose-exclude
とともに使用してみました
私はテスト自体を高速化する方法について多くのことを読みましたが、データベースの初期化を最適化または回避する方法に関するリードを見つけていません。私は、データベースでテストしないようにするための提案を見てきましたが、それを完全に回避する方法を知ることはできません。
もし私に知らせてください
繰り返しますが、テスト自体を高速化する方法についてのヘルプは必要ありませんが、初期化(またはオーバーヘッド)は必要です。上記の例では、80秒ではなく10秒かかります。
どうもありがとう
--verbose 3
を使用してテスト(単一のアプリ)を実行しましたが、これはすべて移行に関連していることがわかりました。
Rendering model states... DONE (40.500s)
Applying authentication.0001_initial... OK (0.005s)
Applying account.0001_initial... OK (0.022s)
Applying account.0002_email_max_length... OK (0.016s)
Applying contenttypes.0001_initial... OK (0.024s)
Applying contenttypes.0002_remove_content_type_name... OK (0.048s)
Applying s3video.0001_initial... OK (0.021s)
Applying s3picture.0001_initial... OK (0.052s)
... Many more like this
私はすべての移行を押しつぶしましたが、まだ遅いです。
私の問題を修正する最後のソリューションは、テスト中に強制的にDjangoに移行を無効にすることです。これは、このような設定から実行できます
TESTING = 'test' in sys.argv[1:]
if TESTING:
print('=========================')
print('In TEST Mode - Disableling Migrations')
print('=========================')
class DisableMigrations(object):
def __contains__(self, item):
return True
def __getitem__(self, item):
return "notmigrations"
MIGRATION_MODULES = DisableMigrations()
または https://pypi.python.org/pypi/Django-test-without-migrations を使用します
テスト全体に約1分かかり、小さなアプリには5秒かかります。
私の場合、移行時にテストを更新するので、テストに移行は必要ありません。また、データを追加するために移行を使用しません。これは誰にとってもうまくいくわけではありません
pytest
を使用してください!
pip install pytest-Django
pytest --nomigrations
の代わりに./manage.py test
./manage.py test
費用2分11.86秒pytest --nomigrations
コスト2.18秒プロジェクトのルートディレクトリにpytest.ini
というファイルを作成し、そこに デフォルトのコマンドラインオプション および/または Django設定 を指定できます。
# content of pytest.ini
[pytest]
addopts = --nomigrations
Django_SETTINGS_MODULE = yourproject.settings
これで、pytest
を使用してテストを実行するだけで、入力の手間を省くことができます。
--reuse-db
をデフォルトのコマンドラインオプションに追加することにより、後続のテストをさらに高速化できます。
[pytest]
addopts = --nomigrations --reuse-db
ただし、データベースモデルが変更されたらすぐに、pytest --create-db
を1回実行して テストデータベースの再作成を強制 する必要があります。
テスト中に geventモンキーパッチング を有効にする必要がある場合、プロジェクトルートディレクトリに次の内容のpytest
というファイルを作成し、実行ビットをキャストできます(chmod +x pytest
)そしてpytest
の代わりに./pytest
を実行してテストします:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# content of pytest
from gevent import monkey
monkey.patch_all()
import os
os.environ.setdefault("Django_SETTINGS_MODULE", "yourproject.settings")
from Django.db import connection
connection.allow_thread_sharing = True
import re
import sys
from pytest import main
if __== '__main__':
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())
Geventモンキーのパッチ適用が成功したかどうかをテストするためのtest_gevent.py
ファイルを作成できます。
# -*- coding: utf-8 -*-
# content of test_gevent.py
import time
from Django.test import TestCase
from Django.db import connection
import gevent
def f(n):
cur = connection.cursor()
cur.execute("SELECT SLEEP(%s)", (n,))
cur.execute("SELECT %s", (n,))
cur.fetchall()
connection.close()
class GeventTestCase(TestCase):
longMessage = True
def test_gevent_spawn(self):
timer = time.time()
d1, d2, d3 = 1, 2, 3
t1 = gevent.spawn(f, d1)
t2 = gevent.spawn(f, d2)
t3 = gevent.spawn(f, d3)
gevent.joinall([t1, t2, t3])
cost = time.time() - timer
self.assertAlmostEqual(cost, max(d1, d2, d3), delta=1.0,
msg='gevent spawn not working as expected')
参考文献
./ manage.py test --keepdb 移行ファイルに変更がない場合
データベースの初期化には本当に時間がかかります...
私は、ほぼ同数のモデル/テーブル(約77)、および約350のテストを含むプロジェクトを行っており、すべてを実行するのに合計1分かかります。 2 cpusが割り当てられ、2 GBのRAMを搭載した浮浪者マシンでの開発。また、py.testとpytest-xdistプラグインを使用して、複数のテストを並行して実行します。
もう1つできることは、tell Djangoテストデータベースを再利用し、スキーマが変更された場合にのみ再作成します。また、テストでメモリ内データベースを使用するようにSQLiteを使用できます。両方のアプローチをここで説明します: https://docs.djangoproject.com/en/dev/topics/testing/overview/#the-test-database
[〜#〜] edit [〜#〜]:上記のオプションのいずれも機能しない場合、ユニットテストにDjango SimpleTestCaseを使用するか、この回答で説明されているデータベースを作成しないカスタムテストランナーを使用してください。 dbなしのDjangoユニットテスト 。
次に、Djangoこのようなライブラリを使用してデータベースへの呼び出しをモックすることができます(これは間違いなく書きました): https://github.com/stphivos/Django-mock-クエリ
このようにして、ユニットテストをローカルで高速に実行し、CIサーバーに、データベースを必要とする統合テストの実行を心配させてから、本番ではない安定したdev/masterブランチにコードをマージできます。