Shoeisha Technology Media

CodeZine(コードジン)

記事種別から探す

Djangoのテストの書き方とCircleCIを活用した継続的インテグレーション

モダンDjango入門 第3回

  • LINEで送る
  • このエントリーをはてなブックマークに追加
2018/08/03 14:00

 日本でカンファレンスも開催されて、ますます盛り上がりを見せているDjango。この連載では最新のDjangoとDjango REST frameworkを用いたプロダクト開発の手法を、実務に使える形でお伝えしていきます。今回はまずDjangoの実践的なテストの書き方と、CircleCIを使った継続的インテグレーションを紹介します。

目次

今回の内容

 前回は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間の継承関係を表したものです。

unittestとDjangoの各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.TestCaseself.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

  • LINEで送る
  • このエントリーをはてなブックマークに追加

著者プロフィール

  • 新井 正貴(アライ マサタカ)

     東京大学文学部卒業、アライドアーキテクツ株式会社にて勤務。2016年4月、株式会社SQUEEZEに入社。コミュニティ活動として、PyCon JP 2015〜 スタッフ、Pythonもくもく会の主催を行う。趣味はラクロスとPerfume。 Site:http://massa14...

バックナンバー

連載:モダンDjango入門
All contents copyright © 2005-2018 Shoeisha Co., Ltd. All rights reserved. ver.1.5