Django
ユニットテスト
サーチ…
テスト - 完全な例
これは、新しいDjangoプロジェクトの開始に関するドキュメントを読んだことを前提としています。プロジェクトのメインアプリケーションの名前がtd(テスト駆動の略)であると仮定します。最初のテストを作成するには、test_view.pyという名前のファイルを作成し、次の内容をコピーしてコピーします。
from django.test import Client, TestCase
class ViewTest(TestCase):
def test_hello(self):
c = Client()
resp = c.get('/hello/')
self.assertEqual(resp.status_code, 200)
このテストは、
./manage.py test
それは最も自然に失敗するでしょう!次のようなエラーが表示されます。
Traceback (most recent call last):
File "/home/me/workspace/td/tests_view.py", line 9, in test_hello
self.assertEqual(resp.status_code, 200)
AssertionError: 200 != 404
なぜそれが起こるのですか?私たちはそれについてのビューを定義していないので!だからそれをやりましょう。 views.pyという名前のファイルを作成し、次のコードを配置します
from django.http import HttpResponse
def hello(request):
return HttpResponse('hello')
次に、次のようにurls pyを編集して/ hello /にマップします:
from td import views
urlpatterns = [
url(r'^admin/', include(admin.site.urls)),
url(r'^hello/', views.hello),
....
]
テストをもう一度やり直して./manage.py test
again and viola !!
Creating test database for alias 'default'...
.
----------------------------------------------------------------------
Ran 1 test in 0.004s
OK
Djangoモデルを効果的にテストする
クラスを想定する
from django.db import models
class Author(models.Model):
name = models.CharField(max_length=50)
def __str__(self):
return self.name
def get_absolute_url(self):
return reverse('view_author', args=[str(self.id)])
class Book(models.Model):
author = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
private = models.BooleanField(default=false)
publish_date = models.DateField()
def get_absolute_url(self):
return reverse('view_book', args=[str(self.id)])
def __str__(self):
return self.name
テスト例
from django.test import TestCase
from .models import Book, Author
class BaseModelTestCase(TestCase):
@classmethod
def setUpClass(cls):
super(BaseModelTestCase, cls).setUpClass()
cls.author = Author(name='hawking')
cls.author.save()
cls.first_book = Book(author=cls.author, name="short_history_of_time")
cls.first_book.save()
cls.second_book = Book(author=cls.author, name="long_history_of_time")
cls.second_book.save()
class AuthorModelTestCase(BaseModelTestCase):
def test_created_properly(self):
self.assertEqual(self.author.name, 'hawking')
self.assertEqual(True, self.first_book in self.author.book_set.all())
def test_absolute_url(self):
self.assertEqual(self.author.get_absolute_url(), reverse('view_author', args=[str(self.author.id)]))
class BookModelTestCase(BaseModelTestCase):
def test_created_properly(self:
...
self.assertEqual(1, len(Book.objects.filter(name__startswith='long'))
def test_absolute_url(self):
...
いくつかの点
-
created_properly
テストは、djangoモデルの状態プロパティを検証するために使用されます。それらは、デフォルト値、file_upload_pathsなどを変更した場所のキャッチアップに役立ちます。 -
absolute_url
は自明ではないかもしれませんが、URLパスを変更するとバグを防ぐことができました - 私は同様にモデル内で実装されたすべてのメソッドのテストケースを記述します(
mock
オブジェクトなどを使用して) - 共通の
BaseModelTestCase
を定義することで、適切なテストを確実に行うためにモデル間に必要な関係を設定することができます。
最後に、疑わしいときは、テストを書く。軽微な動作の変化は細部に注意を払うことによって捕らえられ、長く忘れたコードは不要なトラブルを引き起こすことはありません。
Djangoビューでのアクセス制御のテスト
tl; dr :2つのユーザーオブジェクト( user
とanother_user
)を定義する基本クラスを作成します。他のモデルを作成し、3つのClient
インスタンスを定義します。
-
self.client
:ブラウザにログインしているuser
表す -
self.another_client
:another_user
のクライアントを表す -
self.unlogged_client
:未ログインの人を表す
これらの3つのクライアントオブジェクトからすべてのあなたの公開URLと私的URLにアクセスし、あなたが期待する応答を指示します。以下では、 private
(少数の特権ユーザーが所有)またはpublic
(誰でも見ることができる)のいずれかのBook
オブジェクトの戦略を紹介しました。
from django.test import TestCase, RequestFactory, Client
from django.core.urlresolvers import reverse
class BaseViewTestCase(TestCase):
@classmethod
def setUpClass(cls):
super(BaseViewTestCase, cls).setUpClass()
cls.client = Client()
cls.another_client = Client()
cls.unlogged_client = Client()
cls.user = User.objects.create_user(
'dummy',password='dummy'
)
cls.user.save()
cls.another_user = User.objects.create_user(
'dummy2', password='dummy2'
)
cls.another_user.save()
cls.first_book = Book.objects.create(
name='first',
private = true
)
cls.first_book.readers.add(cls.user)
cls.first_book.save()
cls.public_book = Template.objects.create(
name='public',
private=False
)
cls.public_book.save()
def setUp(self):
self.client.login(username=self.user.username, password=self.user.username)
self.another_client.login(username=self.another_user.username, password=self.another_user.username)
"""
Only cls.user owns the first_book and thus only he should be able to see it.
Others get 403(Forbidden) error
"""
class PrivateBookAccessTestCase(BaseViewTestCase):
def setUp(self):
super(PrivateBookAccessTestCase, self).setUp()
self.url = reverse('view_book',kwargs={'book_id':str(self.first_book.id)})
def test_user_sees_own_book(self):
response = self.client.get(self.url)
self.assertEqual(200, response.status_code)
self.assertEqual(self.first_book.name,response.context['book'].name)
self.assertTemplateUsed('myapp/book/view_template.html')
def test_user_cant_see_others_books(self):
response = self.another_client.get(self.url)
self.assertEqual(403, response.status_code)
def test_unlogged_user_cant_see_private_books(self):
response = self.unlogged_client.get(self.url)
self.assertEqual(403, response.status_code)
"""
Since book is public all three clients should be able to see the book
"""
class PublicBookAccessTestCase(BaseViewTestCase):
def setUp(self):
super(PublicBookAccessTestCase, self).setUp()
self.url = reverse('view_book',kwargs={'book_id':str(self.public_book.id)})
def test_user_sees_book(self):
response = self.client.get(self.url)
self.assertEqual(200, response.status_code)
self.assertEqual(self.public_book.name,response.context['book'].name)
self.assertTemplateUsed('myapp/book/view_template.html')
def test_another_user_sees_public_books(self):
response = self.another_client.get(self.url)
self.assertEqual(200, response.status_code)
def test_unlogged_user_sees_public_books(self):
response = self.unlogged_client.get(self.url)
self.assertEqual(200, response.status_code)
データベースとテスト
Djangoはテスト時に特別なデータベース設定を使用して、テストでデータベースを正常に使用できるようにしますが、デフォルトでは空のデータベースで実行します。あるテストでのデータベースの変更は、別のテストでは認識されません。たとえば、次の両方のテストが合格します。
from django.test import TestCase
from myapp.models import Thing
class MyTest(TestCase):
def test_1(self):
self.assertEqual(Thing.objects.count(), 0)
Thing.objects.create()
self.assertEqual(Thing.objects.count(), 1)
def test_2(self):
self.assertEqual(Thing.objects.count(), 0)
Thing.objects.create(attr1="value")
self.assertEqual(Thing.objects.count(), 1)
備品
複数のテストでデータベースオブジェクトを使用する場合は、テストケースのsetUp
メソッドでデータベースオブジェクトを作成するか、さらに、djangoプロジェクトでフィクスチャを定義した場合は、以下のようにフィクスチャを組み込むことができます:
class MyTest(TestCase):
fixtures = ["fixture1.json", "fixture2.json"]
デフォルトでは、djangoは各アプリのfixtures
ディレクトリにあるfixtures
チャを探しています。さらにディレクトリはFIXTURE_DIRS
設定を使って設定できます:
# myapp/settings.py
FIXTURE_DIRS = [
os.path.join(BASE_DIR, 'path', 'to', 'directory'),
]
次のようにモデルを作成したとします。
# models.py
from django.db import models
class Person(models.Model):
"""A person defined by his/her first- and lastname."""
firstname = models.CharField(max_length=255)
lastname = models.CharField(max_length=255)
それで、あなたの.jsonのフィクスチャは次のようになります:
# fixture1.json
[
{ "model": "myapp.person",
"pk": 1,
"fields": {
"firstname": "Peter",
"lastname": "Griffin"
}
},
{ "model": "myapp.person",
"pk": 2,
"fields": {
"firstname": "Louis",
"lastname": "Griffin"
}
},
]
テストデータベースを再利用する
テスト実行のスピードを上げるために、管理コマンドにテストデータベースを再利用するよう指示することができます(また、テスト実行のたびに作成されたり削除されることを防ぐため)。これは、以下のようにkeepdb(またはshorthand -k
)フラグを使って行うことができます:
# Reuse the test-database (since django version 1.8)
$ python manage.py test --keepdb
実行されるテストの数を制限する
テストランナーがどのモジュールを発見するべきかを指定することによって、 manage.py test
によって実行されるテストを制限することができます:
# Run only tests for the app names "app1"
$ python manage.py test app1
# If you split the tests file into a module with several tests files for an app
$ python manage.py test app1.tests.test_models
# it's possible to dig down to individual test methods.
$ python manage.py test app1.tests.test_models.MyTestCase.test_something
たくさんのテストを実行したい場合は、ファイル名のパターンを渡すことができます。たとえば、モデルに関係するテストのみを実行するとします。
$ python manage.py test -p test_models*
Creating test database for alias 'default'...
.................................................
----------------------------------------------------------------------
Ran 115 tests in 3.869s
OK
最後に、-- --failfast
を使用して、最初の失敗時にテストスイートを停止することができます。この引数を指定すると、スイートで発生した潜在的なエラーを素早く取得できます。
$ python manage.py test app1
...F..
----------------------------------------------------------------------
Ran 6 tests in 0.977s
FAILED (failures=1)
$ python manage.py test app1 --failfast
...F
======================================================================
[Traceback of the failing test]
----------------------------------------------------------------------
Ran 4 tests in 0.372s
FAILED (failures=1)