今回の内容
前回はDjango 2.0での変更点を取り上げ、モデルの定義、Django REST frameworkの導入ついて解説しました。前回までのコードは以下から取得できます。
第3回となる今回は、まず前回実装したViewに対するテストを取り上げます。Djangoの標準的なテストから始めて、サードパーティを利用したより実践的なテストの書き方を紹介します。その次に、実装したテストをCircleCIでの継続的インテグレーションに組み込みます。
テストの作成
前回Django REST frameworkのModelViewSetを用いて実装したkanban/board/views.pyの品質を担保するために、テストを書いてみましょう。
python manage.py startappを実行した際にtests.pyというテスト用のファイルが作成されているので、まずはそこにテストを書いていきます。
TestCase
DjangoではPython標準のunittest.TestCaseを拡張したものであるdjango.test.TestCaseが用意されています。次の図は、unittestとDjangoの各TestCase間の継承関係を表したものです。
django.test.TestCaseには、Djangoアプリケーションのテストを書くうえで役立つ機能が以下のように数多く備わっています。
- フィクスチャ読み込みによるテストデータ生成
- settingsの上書き機能
- 独自のアサーションメソッド
- タギングによるテストの分類
- メール送信をダミーの送信箱に振り分け
- 各テストを実行する度にDBをロールバック
それでは、データをPOSTしてチケットが正常に作成されるかを確かめるテストを書いてみます。
import json
from django.test import TestCase
from django.urls import reverse
from rest_framework import status
from kanban.board.models import Ticket
class TicketTests(TestCase):
def test_post(self):
url = reverse('ticket-list')
data = {
'assignee': None,
'name': 'test ticket',
'description': 'This is a test ticket.',
'status': 1,
'start': '2018-06-01',
'end': None,
}
response = self.client.post(url, json.dumps(data), content_type='application/json')
self.assertEqual(response.status_code, status.HTTP_201_CREATED)
self.assertEqual(Ticket.objects.count(), 1)
self.assertEqual(Ticket.objects.get().name, 'test ticket')
django.test.TestCaseのself.clientはデフォルトで、django.test.Clientのインスタンスが格納されています。django.test.Clientとは、ダミーのブラウザのように振る舞い、DjangoのHTTP関連のテストを助けるものです。
self.client.post(url, json.dumps(data), 'application/json')とすることで、http://localhost:8000/api/tickets/に向けて、以下のJSONをPOSTできます。
{
"assignee": null,
"name": "test ticket",
"description": "This is a test ticket.",
"status": 1,
"start": "2018-06-01",
"end": null
}
self.assertEqual()を使って、以下の観点から期待した値かどうかをチェックしています。
- ステータスコードが201かどうか
- 作成されたレコードが1つかどうか
- 作成されたチケットのname属性が正しいかどうか
ここでは1つのテストケースに複数のassertを実行していますが、途中のアサーションで失敗するとそれ以降のアサーションは実行されません。この方式とは別に、テストケース毎に1つのアサーション(one assertion per test)で実装するやり方もあります。この方式の利点は、どのデータが原因でテストが失敗しているのかわかりやすくなる点や、どのテストケースが落ちているかを正しくレポートできる点にあります。
テストの実行には、python manage.py test <ラベル名>コマンドを使います。
(env) $ python manage.py test kanban Creating test database for alias 'default'... System check identified no issues (0 silenced). . ---------------------------------------------------------------------- Ran 1 test in 0.066s OK
python manage.py testの引数には、Pythonのモジュールなどを指定するようになっています。以下のようにテストケースやテストメソッドを指定することで、実行したいテストの範囲を絞ることも可能です。今のところテストメソッドは1つしか実装していないため、以下の4つはすべて同じ振る舞いになります。
- python manage.py test kanban
- python manage.py test kanban.board
- python manage.py test kanban.board.tests.TicketTests
- python manage.py test kanban.board.tests.TicketTests.test_post
