flaskでWebアプリ開発のチュートリアル、「Test coverage」を進めていきます。現在やっているチュートリアルは次のページの「authentication」になります。
今回は、Webアプリケーションの認証関連のプログラムauth.pyを検証するところから進めていきます。
こんな人の役に立つかも
・PythonでWebアプリケーションを作成したい人
・flaskのチュートリアルを行なっている人
・flaskのチュートリアルでブログアプリflaskrを作成している人
復習事項
テストのプログラムの階層はこのような感じです。
setup.cfgはテストのチュートリアルの最後に出てきますが、随時実行して確認したい場合は必要となります。
pythonの仮想環境は「flaskr」と「tests」フォルダと同じ階層(flask-tutoriak)階層にて起動してください。
Authentication
このテストでは、flaskrの「auth.py」に記載されたログイン処理、ログアウト処理、ユーザー登録処理など、認証に関する処理のテストを行うプログラム「test_auth.py」を作成していきます。
「auth」fixtureの作成
まずは、ログイン処理、ログアウト処理を簡単に行うことができるように、「conftest.py」に次のようなfixtureを定義します。
#①ログイン処理とログアウト処理を簡易に行うためのクラスです。
class AuthActions(object):
#インスタンス化で「client」fixtureが_clientに渡されます。
def __init__(self, client):
self._client = client
#ログイン処理をPOSTします。POSTで渡す値は固定値とします。
def login(self, username='test', password='test'):
return self._client.post(
'/auth/login',
data={'username': username, 'password': password}
)
#ログアウト処理をgetします。
def logout(self):
return self._client.get('/auth/logout')
#②上記クラスへ「client」fixtureを渡して、「auth」fixtureとします。
@pytest.fixture
def auth(client):
return AuthActions(client)
「auth.py」には、POSTメソッドで呼び出されるログイン処理と、GETメソッドで呼び出されるログアウト処理があります。「AuthAction」クラスを作成して、そのクラスのインスタンスに引数として「client」fixtureを渡すことで、auth.loginやauth.logoutとすることで簡単にテストプログラム内でログイン処理、ログアウト処理を行うことができるようになります。
①の「AuthAction」クラスでは、引数に「object」を取ることになっていて、「client」fixtureを受け取る予定の引数です。「client」fixtureは、このクラスのインスタンス化の際に__init__メソッドでself._clientに代入されますので、ログイン処理やログアウト処理では「client」fixtureのサーバーを立てなくてもリクエストを行うことができる「.post」や「.get」を利用することができています。
②では、「AuthAction」クラスをインスタンス化する処理をreturnしてfixtureとして利用することで、authというfixtureでログイン、ログアウト処理が実現する、という構成になっています。
また、この「auth」fixtureをテスト用プログラムの中で利用することでログイン処理とログアウト処理をテストできたことになります。
登録処理の検証
auth.pyの「resister」を検証するテストプログラムになります。「test_auth.py」を「tests」フォルダ内に作成して、次のテストプログラムを入力します。
import pytest
from flask import g, session
from flaskr.db import get_db
#①registerの機能を検証
def test_register(client, app):
#GETメソッドで200のステータスコードが返ってくるかどうか検証
assert client.get('/auth/register').status_code == 200
#POSTメソッドでリクエストします。
response = client.post(
'/auth/register', data={'username': 'a', 'password': 'a'}
)
#ログイン画面へリダイレクトしたかどうかを確認します。
assert 'http://localhost/auth/login' == response.headers['Location']
with app.app_context():
assert get_db().execute(
"select * from user where username = 'a'",
).fetchone() is not None
#ユーザー「a」という条件でuserテーブルから1行取り出すことができる。
#②フォームに色々なパターンで値を与える検証
@pytest.mark.parametrize(('username', 'password', 'message'), (
('', '', b'Username is required.'),#パターン1
('a', '', b'Password is required.'),#パターン2
('test', 'test', b'already registered'),#パターン3
))
def test_register_validate_input(client, username, password, message):
#POSTメソッドで上記の3パターンのパラメータを入れてどのようなメッセージが返ってくるかを検証します。
response = client.post(
'/auth/register',
data={'username': username, 'password': password}
)
assert message in response.data
①のプログラムでは、GETメソッドでregisterのURLにアクセスを行い、200というステータスコードが返ってくるかを検証します。
そして、次にPOSTメソッドでユーザー名「a」、パスワード「a」というユーザーを登録します。ユーザー登録ができると、loginへリダイレクトしますので、「.heders[‘location’]」にアクセスして、URLを取得して、それが正しいURLかどうかを確認しています。最後には、アプリケーションコンテキストを立ち上げ、データベースにコネクションを確立してからSELECT文でデータベースにユーザー「a」が追加されているかを確認しています。データベースから取り出したデータがNoneでないことが正しい条件です。
②では、「pytest.mark.parametrize」というデコレータを利用して、一度に複数の値をテストで切るようです。
今回は、
1:何も入力していない時
2:aというユーザー名のみを入力した時
3:すでに存在しているユーザーを登録しようとした時
の3パターンを検証していきます。
「test_register_validate_input」は3つの引数をとり、それぞれusername、password、messageです。この関数で登録フォームにusernameとpasswordを入れることで、messageに与えた文字列が含まれているかどうかを検証しています。
テスト用のプログラム、なんだか疲れてきました・・・・
テスト用のプログラムを作ると、flaskが裏側でどんな風に動いているかもわかってくるね〜
ちょっと長くなってしまいましたので、次は、ログイン処理の検証から行なっていきたいと思います。