28
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Django Restframework のテストコードを作成する

Last updated at Posted at 2018-12-19

はじめに

Django Restframework で開発したAPIに対するテストコードを作成する。
そのために必要なノウハウやパッケージなどについてまとめる。

サンプルコード

こちらに作成したコードを参考に以下の話を進めていく。
sampleapp.tests 配下にテスト用のコードを作成している。

使用したライブラリ等

Pythonは3.8.x

  • Django 3.0.x
  • Django Restframework 3.11.x
  • factory-boy 2.11.x
  • pytest 4.0.x
  • pytest-django 3.4.x

テスト向けのライブラリ説明

factory-boy

テストデータを簡単に作成するためのライブラリ。

https://factoryboy.readthedocs.io/en/latest/
https://github.com/FactoryBoy/factory_boy

pytest, pytest-django

詳細なテスト結果を表示する

https://docs.pytest.org/en/latest/
https://pytest-django.readthedocs.io/en/latest/

テストコードの開発方針(案)

  • APIをリクエストし、レスポンスを検証することで一通りの処理をテストする。
  • Django-Restframework のViewset でデフォルト作成されるAPIのテストは作らない。
  • テストメソッドは、他のテストメソッドに依存しない。
  • テストコードは見た目の分かりやすさ重視。コードの共通化などは避け、冗長でも良いので1つ1つ処理を記述する。(作りこみ過ぎるとテスト自体が怪しくなるため)
  • テストは、settings.DATABASESに SQLite3を指定して行う。SQLite3の場合は、テストデータはテスト時にメモリに展開されるのみで、物理的な場所は不要。

テスト対象のコードについて

サンプルコードを参照。

settings

この例では、 LimitOffsetPagination を定義する。
こちらの定義次第で、返却される値が変わるので、テストコードではそれに応じた検証を行う。

通常、テスト時と本番運用時の settings は分ける。サンプルコードでは、 test_settings.py を用意している。

model, view, serializer

Post モデルのデータを扱うAPIを作成。
Postが使うステータスの PostStatus や 子モデルの Comment をサンプル用に用意。

テストコードの実装

サンプルコードを参照。

Sampleapp の PostモデルのViewset をテストするものとする.

Factoryクラスの準備

ここではテストデータを簡単に作成するためのFactoryクラスを準備する。

Factoryは色々なモジュールで使いまわされることを想定して、 factories.py というモジュールに外出しして、こちらに全てのFactoryクラスを作成する。
基本的には、1modelクラスに対して、1Factoryクラスを作る。

利用しやすい様、 factory.Sequence や  factory.Faker など、
適当な初期値が入る様に定義する。

viewのテスト

ここでviewのテストを定義する。

検証内容

  • Postの検索で、PostSerializerに設定した全属性が取得できることを確認(※)
  • Postの検索で、titleによる前方一致検索が出来ることを確認
  • Postの登録で、1件保存できることを確認(※)

上記、テストコードの開発方針(案) で 「Django-Restframework のViewset でデフォルト作成されるAPIのテストは作らない」と記述しているが、サンプルのため敢えてテストコードを作成している。

テスト用のモジュール、クラス、メソッドの分割単位

(テストではない)実コードは、1modelクラスに対して、1viewモジュールを作っている。

  • 1viewモジュールに対して、1testモジュールを作成
  • 一覧検索系のテストをまとめるテストクラス、1件のCRUDのテストをまとめるテストクラスを作成
  • 各クラス内で、対象のテストケース毎にテストメソッドを作成。

Factory によるテストデータの作成

Factoryクラスのインスタンス化で、それぞれのモデルのテストデータが作成される。

# 以下の記載だけでFactoryクラスの定義に従ってデータが出来る
factories.PostFactory()
# titleを明示的に設定したい場合は以下のように定義
factories.PostFactory(title="abcde")

Factoryクラスに、初期値を定義しているので、テストメソッド内で検証する必要がない属性については、明示的に指定しなくて済む。保守性を考え、明示的な指定は必要最低限にして、後はFactoryの初期値に任せる。

as_view() による実行APIの指定

Viewsetのテストでは、 as_view でViewsetで定義されるCRUDのAPIが全て呼び出せる。
以下のコード例では、一覧検索の例しかないが、 as_viewのパラメータを変えることで、別のAPIを呼ぶことが出来る。

例) {'put', 'update'} で更新用のAPIが呼べる。下のリンク参照。
https://www.django-rest-framework.org/api-guide/routers/#simplerouter

reverse によるパラメータの指定

また、Restful API ではパラメータの渡し方がいくつかあるが(query, path, body など)、
それぞれ、以下のリンクで設定方法が分かるので参考まで。

APIRequestFactoryによるリクエストの実行

APIRequestFactory を使って、リクエストを実行する。

from django.urls import reverse
from rest_framework.test import APIRequestFactory

# 略

factory = APIRequestFactory()
post_list = PostViewSet.as_view({'get': 'list'})
url = "".join([
            reverse('sampleapp:post-list')
        ])
request = factory.get(url)
response = post_list(request)

テストの実行

python manage.py test [app_name] --settings=drf_sample.test_settings でもテスト出来るが、ここでは pytest を使う。

pytest -v --ds=sampleapp.test_settings

pytest によって、PASSしたテストメソッドが明示され、PASSしなかった場合、詳細な情報が表示される。以下の様な出力がされれば成功。

sampleapp/tests/views/test_post_views.py::PostListTests::test_search_post PASSED [ 33%]
sampleapp/tests/views/test_post_views.py::PostListTests::test_search_post_by_title PASSED [ 66%]
sampleapp/tests/views/test_post_views.py::PostDetailTests::test_create_post PASSED [100%]

pytest.ini

pytest用の設定を行う

  • テスト用のsettingsを読み込ませる
  • テスト用のモジュールのワイルドカードを定義する

この設定をすれば、 pytest -v でテスト出来る。

個人的にハマったところ

  • tests配下の各ディレクトリに、 __init__.py を置き忘れて何のテストも実行されない(初歩的・・・)
  • test用のモジュール名を pytest.ini に設定したワイルドカードに合わない tests_xxx.py としてしまい何のテストも実行されない。

今後の更新予定

こちらのドキュメントは必要に応じて順次、追記していく。
ひとまずテストの取っ掛かりやコード例を示す。

  • 異常系のテストサンプルも作成。
  • mock を使ったテストなど、まだ記述したい内容あり。順次追加する。
28
38
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
28
38

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?