Django テストで requests に Mock でパッチを当ててテストを実行します。
例えば、インターネットに出れない環境で外部 API をテストすると、接続エラーとなり、テストをすることができません。
そんな時は、Python の Mock モジュールを使います。Mock モジュールは、指定した関数を置き換えることができるので、そういったケースでもテストをすることができます。
また、API が開発中で利用できない場合でも、テストをすることができます。
Contents
バージョン
- Python 3.7.3
- Django 2.2.1
- requests 2.22.0
- beautifulsoup4 4.7.1
インストール
Python はすでにインストールされているものとします。
Python 仮想環境の作成
$ python3 -m venv venv $ source venv/bin/activate
はじめに、Python の仮想環境を作成し、アクティベートします。
Django と requests のインストール
(venv) $ pip install django requests beautifulsoup4
Python の仮想環境を作成したら、そこに必要なパッケージをインストールします。
Django プロジェクトのインストール
(venv) $ django-admin startproject django_project (venv) $ cd django_project/ (venv) $ python manage.py startapp external_api
Django のインストールが終わったら、django-admin コマンドを使って Django プロジェクトのインストールをします。
実装
settings.py
... INSTALLED_APPS = [ ... 'external_api.apps.ExternalApiConfig' ] ...
今回の settings.py は、サンプルのテストを実行するための最低限の箇所だけにしています。
urls.py
from django.urls import path from external_api.views import get_html urlpatterns = [ path('get-html/', get_html), ]
/get-html/ という URL を作成しました。この URL にアクセスすると、GET パラメータで指定した URL の HTML を取得するようにしたいと思います。
views.py
from django.http import HttpResponse import requests def get_html(request): url = request.GET.get('url') if url: res = requests.get(url) res.raise_for_status() return HttpResponse(res.content, content_type=res.headers.get('content-type', 'text/plain')) else: return HttpResponse('None')
/get-html/ の 処理です。GET パラメータ url で指定された URL の HTML を requests を使って取得し、その内容をレスポンスとして返します。
テスト
それでは、本題のテストを実装したいと思います。
tests.py
import requests from bs4 import BeautifulSoup from django.test import TestCase, Client from unittest import mock mock_res = requests.Response() mock_res.status_code = 200 mock_res._content = "<title>Yahoo! JAPAN</title>" class ExternalApiTest(TestCase): @mock.patch('external_api.views.requests.get', mock.MagicMock(return_value=mock_res)) def test_get_html(self): url = 'http://yahoo.co.jp' res = Client().get('/get-html/?url=' + url) self.assertEqual(res.status_code, 200) soup = BeautifulSoup(res.content, 'html.parser') self.assertEqual(soup.title.string, 'Yahoo! JAPAN')
/get-html/ をテストします。GET パラメータ url は http://yahoo.co.jp を指定していますので、これを普通にテストすると、yahoo.co.jp にリクエストが行ってしまいます。冒頭でも言いましたが、インターネットに出れない環境でテストする場合は、エラーになりますので、mock.patch デコレータを使用します。
第一引数は、パッチを当てる関数の Python のパスになります。そして、第二引数の return_value に戻り値を渡します。
これで、インターネットに出れない環境でもテストすることができます。
動作確認
実装が全て終わったので、動作確認していきます。
テストの実行
(venv) $ python manage.py test Creating test database for alias 'default'... System check identified no issues (0 silenced). . ---------------------------------------------------------------------- Ran 1 test in 0.007s OK Destroying test database for alias 'default'...
manage.py の test コマンドを使って、Django のテストを実行します。
テストを実行すると、上記のように結果が OK になりました。インターネットに出れる環境であれば、mock.patch デコレータをコメントアウトして、テストを実行しても同じ結果になります。
まとめ
Django テストで requests に Mock でパッチを当ててテストを実行しました。
インターネットに出れない環境や開発中の API をテストする場合、Mock でパッチを当てることにより、指定した関数を置き換えることができるので、テストすることができました。
また、戻り値を好きに変えることができるので、様々なテストができるようになり、とても便利です。