色々なテストコードを作成してみよう の講座の4レッスン目。
今回はAPIの自動テスト方法を学んで行きます。
前回のレッスンはこちら↓
前回はバッチの自動テスト方法を学んだな
あと。。。なんかあるっけ?
重要なテストがまだあるぞ。
APIのテストだ。
今回はお前の日記アプリにAPIを追加して、テストコードを書いてみるぞ
テスト対象の準備
例のごとくお前のクソ日記アプリの雛形をクローンしておくぞ。
リポジトリのクローン
$ git clone https://github.com/yheihei/base_diary.git
$ cd base_diary
テストコードだけ見る場合
正解を書いたブランチは下記だ。テストコードだけ見たいやつは見てくれ
# テストコードがすでに書いてあるブランチを見たい場合はこちら
$ git fetch && git checkout feature/#5
テスト対象の解説
忘れてると思うから、もう一度日記アプリの仕様を確認する。
トップページにアクセスすると、こんな画面が出てくる
仕様は下記。
- トップページにアクセスすると、日記一覧が表示される
- 各日記には下記が表示される
- タイトル
- 投稿者
- 本文
- 投稿日
- カテゴリー
今回実装する機能
今回は投稿のAPIを作ってみる。エンドポイントと機能は下記の通りだ
method | エンドポイント | 名前 | どんな動作か |
---|---|---|---|
GET | /api/posts/ | 投稿一覧取得 | ・全記事のリストを取得できる ・タイトルと本文をキーワード検索できる ・更新時刻、作成時刻で並び替えができる ・カテゴリで検索できる |
POST | /api/posts/ | 新規投稿 | ・タイトルと本文を指定して記事を投稿できる |
GET | /api/posts/:id/ | 投稿取得 | ・特定の記事のタイトル、本文、カテゴリ、投稿者情報を取得できる |
PUT | /api/posts/:id/ | 投稿更新 | ・特定の記事を指定したタイトル、本文で更新できる |
PATCH | /api/posts/:id/ | 投稿一部更新 | ・特定の記事を、タイトルだけ、本文だけ、など一部のフィールドだけ更新できる |
DELETE | /api/posts/:id/ | 投稿削除 | ・特定の記事を削除できる |
※ 全APIエンドポイントは投稿者に紐ついたトークン認証もしくはセッションによるログイン認証を行わなければならない
※ 認証に失敗したらステータスコード401が返却され、動作に失敗する
例えば、トークン認証をしながら、投稿一覧取得のAPIを叩くと、こんな感じに返ってくる
$ curl -X GET http://127.0.0.1:8000/api/posts/ -H 'Authorization: Token 18c26d59a34efb80ee100d55642aa644b1dc9a52'
[
{
"id": 1,
"user": {
"id": 1,
"username": "yhei",
"email": "yheihei0126@gmail.com"
},
"title": "一つ目の日記",
"body": "最初の日記だよ",
"created_at": "2021-07-13T05:10:40.097732Z",
"updated_at": "2021-07-24T05:31:37.578064Z",
"categories": [
{
"name": "日記",
"slug": "diary"
}
]
},
{
"id": 2,
"user": {
"id": 1,
"username": "yhei",
"email": "yheihei0126@gmail.com"
},
"title": "二つ目の日記",
"body": "この日記は2つ目の日記だよ",
"created_at": "2021-07-04T05:11:13.196329Z",
"updated_at": "2021-07-22T13:59:50.294969Z",
"categories": [
{
"name": "雑記",
"slug": "general"
}
]
}
]
機能追加する
めちゃくちゃAPIあんじゃん!?
これ、今から作るの??? めっちゃ長くなるぞ?
うむ。スクラッチで書くのはめんどすぎるので
django rest frameworkを使って、手抜きするぞ
この講座はテストのやり方の解説なので、django rest frameworkについては深くは説明しない。
下記の講座でdjango rest frameworkを使ったAPIの作り方を書いているから、気になるやつはそちらを見てくれ。
必要なパッケージをインストールする
こっからは基本的にコピペで良い
なぜならAPIのレッスンではなく、テストコードのレッスンだから。
気にせず走り抜けろ!
まずはパッケージインストール。
$ pip install djangorestframework
$ pip install django-filter
djangorestframework
はAPIの作成を楽にしてくれるパッケージだ。
少ない記述でリッチなAPIが実現できる。
django-filter
はAPIの検索条件を記述するときに使えるパッケージだ。
2つを組み合わせることで、APIの作成工数が死ぬほど減る。
settings.pyに設定を書く
次はbasediary/settings.py
をいじっていく。
INSTALLED_APPSにrest_framework
、rest_framework.authtoken
、django_filters
のアプリを追加し、django rest frameworkの設定を追加する。
INSTALLED_APPS = [
...
'rest_framework',
'rest_framework.authtoken', #rest_frameworkにトークン認証の機能をつける
'django_filters',
]
...
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
シリアライザーにAPIで返したい値の定義を書く
次にdiary/serializers.py
を作り、APIで返したい値の定義を書く。
from django.db.models import fields
from diary.models import Post, Category, User
from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ('id', 'username', 'email')
class CategorySerializer(serializers.ModelSerializer):
class Meta:
model = Category
fields = ('name', 'slug')
class PostSerializer(serializers.ModelSerializer):
user = UserSerializer(read_only=True)
categories = CategorySerializer(many=True, read_only=True)
class Meta:
model = Post
fields = ('id', 'user', 'title', 'body', 'created_at', 'updated_at', 'categories')
ModelSerializer
を継承してmodelを指定することで、各モデルのプロパティを勝手に返すようになる。
さっき投稿一覧取得のAPIを叩いた時の、下記レスポンスの定義を↑で書いたってわけ。
[
{
"id": 1,
"user": {
"id": 1,
"username": "yhei",
"email": "yheihei0126@gmail.com"
},
"title": "一つ目の日記",
"body": "最初の日記だよ",
"created_at": "2021-07-13T05:10:40.097732Z",
"updated_at": "2021-07-24T05:31:37.578064Z",
"categories": [
{
"name": "日記",
"slug": "diary"
}
]
},
...
...
views.pyにAPIがコールされた時の挙動を書く
続いて、diary/views.py
を下記のように書き換える。
from django.shortcuts import render
# Create your views here.
from .models import Post, User
from rest_framework import viewsets, filters
from .serializers import PostSerializer
from rest_framework.authentication import TokenAuthentication, SessionAuthentication, BasicAuthentication
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import permission_classes, authentication_classes
# Create your views here.
def index(request):
return render(request, 'index.html', {
'posts': Post.objects.all(),
})
class CategoryFilter(filters.BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
if request.query_params.get('category'):
return queryset.filter(categories__slug=request.query_params.get('category'))
return queryset
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
filter_backends = [
filters.SearchFilter,
filters.OrderingFilter,
CategoryFilter,
]
search_fields = ('title', 'body')
ordering_fields = ('created_at', 'updated_at')
def perform_create(self, serializer):
serializer.save(user=self.request.user)
エンドポイントが呼ばれると、PostViewSet
が呼び出される。
その中で、
serializer_class = PostSerializer
で、先ほど書いたシリアライザーをもとにdjango rest frameworkがレスポンスを勝手に作成する。
さらに言えば
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
ここでModelViewSet
を継承しつつ、queryset
でPost(投稿モデル)の取得を定義。
さらに、serializer_class = PostSerializer
でPost(投稿モデル)をmodelにもつシリアライザーを指定する。
すると、django rest frameworkがPost(投稿モデル)に関するCRUDのAPIの処理を勝手に作ってくれる。
urls.pyにエンドポイントの設定を書く
続いて、diary/urls.py
を下記のように書き換える。
from django.urls import path
from django.conf.urls import url, include
from . import views
from rest_framework import routers
from rest_framework.authtoken import views as auth_views
app_name = "diary"
router = routers.DefaultRouter()
router.register(r'posts', views.PostViewSet)
urlpatterns = [
path("", views.index, name="index"),
url(r'^api/', include(router.urls)),
url(r'^api-token-auth/', auth_views.obtain_auth_token),
]
これだけで、下記のエンドポイントが勝手に作られる。
method | エンドポイント | 名前 |
---|---|---|
GET | /api/posts/ | 投稿一覧取得 |
POST | /api/posts/ | 新規投稿 |
GET | /api/posts/:id/ | 投稿取得 |
PUT | /api/posts/:id/ | 投稿更新 |
PATCH | /api/posts/:id/ | 投稿一部更新 |
DELETE | /api/posts/:id/ | 投稿削除 |
django rest frameworkすごすぎだろ。。。
APIの疎通
最後にマイグレーションをして。サーバーを起動する。
$ python manage.py migrate
$ python manage.py runserver
その後、ログアウトした上で http://127.0.0.1:8000/api/posts/
にアクセスしてみてくれ。
なんか認証エラーみたいなのが出たけど???
そうだ。もう一度仕様を振り返ってみよう。
※ 全APIエンドポイントは投稿者に紐ついたトークン認証もしくはセッションによるログイン認証を行わなければならない
※ 認証に失敗したらステータスコード401が返却され、動作に失敗する
認証がされていなければ、APIにアクセスすることはできないんだ。
settings.pyに下記を書いたのを覚えているか?
REST_FRAMEWORK = {
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend'],
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated', # 全API認証必須
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication', # トークン認証
'rest_framework.authentication.SessionAuthentication', # ログインでの認証
)
}
DEFAULT_PERMISSION_CLASSES
に'rest_framework.permissions.IsAuthenticated'
を指定することで、全APIのエンドポイントが認証必須になる。
さらに、DEFAULT_AUTHENTICATION_CLASSES
で認証方法は何よ? ってのを決めている。
'rest_framework.authentication.TokenAuthentication'
を指定することで、トークン認証で認証が行えるようになる。
'rest_framework.authentication.SessionAuthentication'
を指定することで、ログインしているユーザーならAPIにアクセス可能になる。つまりセッションによるログイン認証だ。
だからログアウトした状態だと、APIにアクセスできないのか
そうだ。それじゃあ、ログインした上でもう一度http://127.0.0.1:8000/api/posts/
にアクセスしてみよう。
おおおおお。日記一覧のデータが返ってきた!!!!
トークン認証でのAPI疎通
ブラウザからAPIにアクセスできることは確認できたな?
ただ、このAPIを使う奴はブラウザだけじゃない。
例えばモバイルのアプリが呼び出すかもしれない。
そう言えば、ブラウザ以外の奴がログインするってどうやってやるの?
そんなときに使うのがトークン認証だ。
流れとしては、
- トークン取得のAPIを、ログイン情報と一緒にコール、トークンを取得する
- 取得したトークンをヘッダーにつけて、APIを呼び出す
だ。
実際にcurlコマンドでやってみよう。
まずはトークン取得のAPIをコールする。ログインユーザーとパスワードを入れてコールしてやる。
$ curl -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{"username":"yhei","password":"password"}' http://127.0.0.1:8000/api-token-auth/
{"token":"7e033a902ac180e0231fced74ba58fcd658c7bbf"}
こうするとそのユーザー用のtokenが発行されるので、
次はtokenをヘッダーにつけて投稿一覧取得のAPIを呼んでみる。
$ curl -X GET http://127.0.0.1:8000/api/posts/ -H 'Authorization: Token 7e033a902ac180e0231fced74ba58fcd658c7bbf'
[{"id":1,"user":{"id":1,"username":"yhei","email":"yheihei0126@gmail.com"},"title":"一つ目の日記","body":"最初の日記だよ","created_at":"2021-07-13T05:10:40.097732Z","updated_at":"2021-07-24T05:31:37.578064Z","categories":[{"name":"日記","slug":"diary"}]},{"id":2,"user":{"id":1,"username":"yhei","email":"yheihei0126@gmail.com"},"title":"二つ目の日記","body":"この日記は2つ目の日記だよ","created_at":"2021-07-04T05:11:13.196329Z","updated_at":"2021-07-22T13:59:50.294969Z","categories":[{"name":"雑記","slug":"general"}]},{"id":3,"user":{"id":1,"username":"yhei","email":"yheihei0126@gmail.com"},"title":"title","body":"パッチで更新してみた","created_at":"2021-07-22T15:51:34.655604Z","updated_at":"2021-07-22T17:33:10.124466Z","categories":[]}]
おお、日記のjsonが取れた!!!
API疎通の確認は以上だ
いよいよテストコードを書いていくぞ
テストコードを書く
日記のデータをfixtureで入れる
まずは何においてもテストデータの投入だ。
diary/fixtures/test/test_api_posts.json
を作って、下記のデータを入れる。
[
{
"model": "diary.user",
"pk": 1,
"fields": {
"password": "pbkdf2_sha256$180000$vYCnAMtlYpZj$GlAfVlJZP6X4qTF2mcRKYHnBKOr5cFmWVdE1HVLZJOc=",
"last_login": "2021-07-13T05:09:45.196Z",
"is_superuser": true,
"username": "yhei",
"first_name": "",
"last_name": "",
"email": "yheihei0126@gmail.com",
"is_staff": true,
"is_active": true,
"date_joined": "2021-07-13T05:09:21.149Z",
"groups": [],
"user_permissions": []
}
},
{
"model": "authtoken.token",
"pk": "7e033a902ac180e0231fced74ba58fcd658c7bbf",
"fields": {
"user": 1,
"created": "2021-07-30T00:00:27.143Z"
}
},
{
"model": "diary.post",
"pk": 1,
"fields": {
"user": 1,
"title": "1つめの日記",
"body": "1つめの日記本文",
"created_at": "2021-07-03T05:10:40.097Z",
"updated_at": "2021-07-22T14:22:57.007Z",
"categories": [
1
]
}
},
{
"model": "diary.post",
"pk": 2,
"fields": {
"user": 1,
"title": "2つめの日記",
"body": "2つめの日記本文",
"created_at": "2021-07-04T05:11:13.196Z",
"updated_at": "2021-07-22T13:59:50.294Z",
"categories": [
2
]
}
},
{
"model": "diary.category",
"pk": 1,
"fields": {
"name": "日記",
"slug": "diary",
"created_at": "2021-07-22T13:59:23.525Z",
"updated_at": "2021-07-22T13:59:23.525Z"
}
},
{
"model": "diary.category",
"pk": 2,
"fields": {
"name": "雑記",
"slug": "general",
"created_at": "2021-07-22T13:59:39.153Z",
"updated_at": "2021-07-22T13:59:39.153Z"
}
}
]
日記データを予め入れておくのに加え、
{
"model": "authtoken.token",
"pk": "7e033a902ac180e0231fced74ba58fcd658c7bbf",
"fields": {
"user": 1,
"created": "2021-07-30T00:00:27.143Z"
}
},
↑でトークン認証のトークンも作っておいた。
テストでAPIをコールする際は、ヘッダに7e033a902ac180e0231fced74ba58fcd658c7bbf
をつければ認証できる。
トークン認証を使ったAPIテスト
早速Token認証のテストをやってみる。
tests.pyに下記を書いてみる。
from django.test import TestCase, Client
from diary.models import User, Post
from django.urls import reverse
# Create your tests here.
class TestApiPosts(TestCase):
'''投稿のAPIテスト'''
# 1. fixtureを読み込む
fixtures = ['diary/fixtures/test/test_api_posts.json']
def test_1(self):
'''投稿一覧が取得できるか'''
client = Client()
response = client.get(
# 2. urlを指定
'/api/posts/',
# 3. ヘッダに認証トークンを付与する
HTTP_AUTHORIZATION=f"Token 7e033a902ac180e0231fced74ba58fcd658c7bbf"
)
self.assertEqual(200, response.status_code)
やることは下記。
- fixtureを読み込む
- 投稿一覧のurlを指定
- ヘッダに認証トークンを付与する
の3つだけ。
「3. ヘッダに認証トークンを付与する」をやらないと、認証エラーで401になるはずなので注意だ。
では、テストを実行してみる。
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.034s
OK
トークン認証でもテストできた!
ログイン認証を使ったAPIテスト
次はトークン認証ではなく、ログイン認証でAPIを叩いてみる。
さっきやった、ブラウザでログインしてからAPIを叩くやつだ。
テストコードを下記のように書き換える。
from django.test import TestCase, Client
from diary.models import User, Post
from django.urls import reverse
# Create your tests here.
class TestApiPosts(TestCase):
'''投稿のAPIテスト'''
# 1. fixtureを読み込む
fixtures = ['diary/fixtures/test/test_api_posts.json']
def test_1(self):
'''投稿一覧が取得できるか'''
client = Client()
# 2. ユーザーIDが1のユーザーでログインする
client.force_login(User.objects.get(id=1))
response = client.get(
# 3. urlを指定
'/api/posts/',
)
self.assertEqual(200, response.status_code)
やることは下記。
- fixtureを読み込む
- ユーザーIDが1のユーザーでログインする
- urlを指定
の3つだけ。
force_login
関数を使うと、そのclient
のセッション中、指定したユーザーでログインした状態になる。
パスワードも必要ないので便利だ。
では、テストを実行してみる。
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
.
----------------------------------------------------------------------
Ran 1 test in 0.034s
OK
これでログイン認証でもトークン認証でもAPIのテストができるようになったね
レスポンスのテスト
認証できるようになったので、いよいよレスポンスのテストを書いてみよう。
例えば投稿一覧が取得できるかのテストを書いてみると、こういう感じになる。
def test_1(self):
'''投稿一覧が取得できるか'''
client = Client()
response = client.get(
'/api/posts/',
HTTP_AUTHORIZATION=f"Token 7e033a902ac180e0231fced74ba58fcd658c7bbf"
)
self.assertEqual(200, response.status_code)
self.assertEqual(
[
{
"id": 1,
"user": {
"id": 1,
"username": "yhei",
"email": "yheihei0126@gmail.com"
},
"title": "1つめの日記",
"body": "1つめの日記本文",
"created_at": "2021-07-03T05:10:40.097000Z",
"updated_at": "2021-07-22T14:22:57.007000Z",
"categories": [
{
"name": "日記",
"slug": "diary"
}
]
},
{
"id": 2,
"user": {
"id": 1,
"username": "yhei",
"email": "yheihei0126@gmail.com"
},
"title": "2つめの日記",
"body": "2つめの日記本文",
"created_at": "2021-07-04T05:11:13.196000Z",
"updated_at": "2021-07-22T13:59:50.294000Z",
"categories": [
{
"name": "雑記",
"slug": "general"
}
]
},
],
response.json()
)
response.json()
に投稿一覧のレスポンスがdictのリストで入ってくる。
fixturesで入れた日記データが、きちんと所定のフォーマットで返ってきているかを
self.assertEqual(期待値, response.json())
で確認してやればいいわけだ。
クエリパラメータをつけたテスト
説明していなかったが、投稿一覧のURLにクエリパラメータをつけることで、カテゴリーでの検索が可能だ。
下記のURLにログインした状態でアクセスすると、
http://127.0.0.1:8000/api/posts/?category=diary
こんな感じに、slugが「diary」のカテゴリーを持つ記事だけが取得できる。
つーことで、カテゴリーの検索機能のテストコードを書いてみよう。
こんな感じだ。
def test_2(self):
'''カテゴリで検索できるか'''
client = Client()
response = client.get(
'/api/posts/',
{
'category': 'diary',
},
HTTP_AUTHORIZATION=f"Token 7e033a902ac180e0231fced74ba58fcd658c7bbf"
)
self.assertEqual(200, response.status_code)
self.assertEqual(
[
{
"id": 1,
"user": {
"id": 1,
"username": "yhei",
"email": "yheihei0126@gmail.com"
},
"title": "1つめの日記",
"body": "1つめの日記本文",
"created_at": "2021-07-03T05:10:40.097000Z",
"updated_at": "2021-07-22T14:22:57.007000Z",
"categories": [
{
"name": "日記",
"slug": "diary"
}
]
},
],
response.json()
)
クエリパラメータは、下記のようにClientクラスのgetメソッドの第二引数に辞書型を入れてやれば指定できる。
response = client.get(
'/api/posts/', # URL指定
{
'category': 'diary', # ?category=diary のクエリを追加
},
HTTP_AUTHORIZATION=f"Token 7e033a902ac180e0231fced74ba58fcd658c7bbf"
)
次に期待するレスポンスが得られたかを確認する。
response.json()
のアサートで、slugがdiaryの投稿だけを指定してやる。
self.assertEqual(
[
{
"id": 1,
"user": {
"id": 1,
"username": "yhei",
"email": "yheihei0126@gmail.com"
},
"title": "1つめの日記",
"body": "1つめの日記本文",
"created_at": "2021-07-03T05:10:40.097000Z",
"updated_at": "2021-07-22T14:22:57.007000Z",
"categories": [
{
"name": "日記",
"slug": "diary"
}
]
},
],
response.json()
)
これでOK。
テストも大丈夫そうだ。
$ python manage.py test
Creating test database for alias 'default'...
System check identified no issues (0 silenced).
..
----------------------------------------------------------------------
Ran 2 tests in 0.044s
OK
新規投稿作成のテスト(POST)
今までGETのテストばっかりやってきたが、ここいらでPOSTのテストもやってみよう。
新規投稿のAPIがちょうどPOSTを扱っている。
method | エンドポイント | 名前 |
---|---|---|
GET | /api/posts/ | 投稿一覧取得 |
POST | /api/posts/ | 新規投稿 |
GET | /api/posts/:id/ | 投稿取得 |
PUT | /api/posts/:id/ | 投稿更新 |
PATCH | /api/posts/:id/ | 投稿一部更新 |
DELETE | /api/posts/:id/ | 投稿削除 |
新規投稿のエンドポイントは
/api/posts/
に- POSTメソッドで
- titleとbodyをリクエストボディーにjsonでつめてやると
- 認証したユーザーを投稿者として、新規の記事を作ってくれる
APIだ。
これらが実行できるか、テストコードで検証してみよう。
こんな感じだ。
def test_5(self):
'''新規投稿できるか'''
client = Client()
# 1. postメソッドでPOSTできる
response = client.post(
'/api/posts/',
# 2. 第二引数に辞書型を入れると、リクエストボディーに指定する値が定義される
{
'title': 'title for test_6',
'body': 'body for test_6',
},
content_type='application/json',
HTTP_AUTHORIZATION=f"Token 7e033a902ac180e0231fced74ba58fcd658c7bbf"
)
self.assertEqual(201, response.status_code)
post = Post.objects.get(title='title for test_6')
self.assertEqual('body for test_6', post.body)
self.assertEqual(User.objects.first(), post.user)
テスト実行は省略するが、各自やってみてくれ。
残りのテストを完成させてみよう
OK!
ここまででAPIのテストは一通りできるようになった
あとは残りの機能のテストを自分の手で完成させてみようか
method | エンドポイント | 名前 | どんな動作か |
---|---|---|---|
GET | /api/posts/ | 投稿一覧取得 | ・(済) ・タイトルと本文をキーワード検索できる ・更新時刻、作成時刻で並び替えができる ・(済) |
POST | /api/posts/ | 新規投稿 | ・(済) |
GET | /api/posts/:id/ | 投稿取得 | ・特定の記事のタイトル、本文、カテゴリ、投稿者情報を取得できる |
PUT | /api/posts/:id/ | 投稿更新 | ・特定の記事を指定したタイトル、本文で更新できる |
PATCH | /api/posts/:id/ | 投稿一部更新 | ・特定の記事を、タイトルだけ、本文だけ、など一部のフィールドだけ更新できる |
DELETE | /api/posts/:id/ | 投稿削除 | ・特定の記事を削除できる |
今までのテストで基本は網羅できた。↑の仕様動作のうち、まだ実装していないテストを自分でやってみよう。
答えは下記にあるが、まずは試行錯誤してみてくれ。
APIの自動テスト方法まとめ
今回は、APIのテスト方法を学んだな
トークン認証は下記のようにヘッダを指定することで可能だ。
response = client.get(
'/api/posts/',
# ヘッダに認証トークンを付与する
HTTP_AUTHORIZATION=f"Token 7e033a902ac180e0231fced74ba58fcd658c7bbf"
)
ログイン認証はforce_login
関数を使うことで実現できる。
# ユーザーIDが1のユーザーでログインする
client.force_login(User.objects.get(id=1))
response = client.get(
# urlを指定
'/api/posts/',
)
GETでクエリストリングを指定するのは下記。
response = client.get(
'/api/posts/',
{
'category': 'diary',
},
HTTP_AUTHORIZATION=f"Token 7e033a902ac180e0231fced74ba58fcd658c7bbf"
)
POSTでリクエストボディにパラメータ指定をするのは下記だ。
response = client.post(
'/api/posts/',
{
'title': 'title for test_6',
'body': 'body for test_6',
},
content_type='application/json',
HTTP_AUTHORIZATION=f"Token 7e033a902ac180e0231fced74ba58fcd658c7bbf"
)
PUTやPATCH、DELETEももちろん可能だ。それは自分で調べてみてくれ。
これでテストコードの書き方はだいたい終わったのかな?
もう一つ、よく使うJWT認証でテストを行う方法を次回やっていくぞ
JWT認証?????
JWT認証ってなに? って顔してるな
\ 講座で学んだことを即アウトプットしよう /
次の講座
Djangoの基礎を学びたい方は
Djangoの基礎を固めたい方はこちら(セール時に買うのがおすすめ)
『Djangoパーフェクトマスター』〜インスタ映えを支えるPython超高速開発Webフレームワークを徹底解説!
動画講座で手を動かしながら、ほとんどのことが学べます。
ウサギもここから始めました。
コメント
[…] APIを自動テストするDjango自動テスト講座第二弾! 関数やクラスをテストす… ウサギ […]