まとめ
Django Rest Frameworkを使っている場合、テストを書くときのRequestFactoryは
rest_framework.test.APIRequestFactory
を使ったほうがいい
起きたこと
テストとpostmanからリクエストを受けたときでRequestオブジェクト内のbodyの型が違ったので調べてみたら、
from django.test import RequestFactory
が期待した形式ではないRequestオブジェクトを渡していた
例
... 前提 ...
使用パッケージ
Django Rest FrameworkでRest APIを作るとする。
ざっくりこんなクラスを用意して、ここにpostを投げるテストを書く。
class SampleViewClass(APIView): def post(self, request): title = request.data.get('param1') items = request.data.get('param2')
Django+RestFrameworkを使っている場合、RequestFactoryの選択肢が2つある。
- from rest_framework.test import APIRequestFactory
- from django.test import RequestFactory
こちらは from django.test import RequestFactory
を使った場合のテストコードである。
from django.test import RequestFactory class TestSampleViewClass(TestCase): def test_param(self): factory = RequestFactory() body = {'param1': 'nanakusa', 'param2': ['seri', 'nazuna', 'gogyou', 'hakobera', 'other'] } request = factory.post('/view', body, format='json') SampleViewClass.as_view()(request)
上記のRequestFactoryを経由してlistを含むjsonを渡した場合に問題が起きる。
SampleViewClass
は以下のように値を受け取る。
class SampleViewClass(APIView): def post(self, request): title = request.data.get('param1') # 'nanakusa' items = request.data.get('param2') # 'other'
items
にはなぜかstring型の 'other'
が入る。
原因は RequestFactory
がRequest.Bodyを QueryDict
型で保持しているため。
QueryDictはListをiteratorとして保持しているため、単純にgetするとlistで取得できず、list内要素の1つだけが返されてしまう。
一方、変数 title
はStringがそのまま取得できていることからわかるように、この現象はiterate処理ができる型のbodyを受け取る場合のみに起きる。
StringやIntで構成されるようなリクエストの場合はどちらを使ってもさほど問題にはならないが、
階層構造があるリクエストを処理する場合に問題になる。
というわけで、Django Rest FrameworkでAPIを作っているときは、テストで使うRequestFactoryメソッドも APIRequestFactory
で合わせたほうが安全。
参照
QueryDictタイプの挙動差異についてはこちらがわかりやすいので参照のこと。