PythonベースのWebアプリケーションフレームワークであるFlaskを用いて開発しているWebアプリについて,その動作テストをしてみたいと思った.調べていると,Pythonのユニットテストライブラリであるnoseを用いることでかなり簡単にテストコードを実装できそうだと分かり,実際にそのWebアプリにもテストを導入してテストカバレッジを94%まで上げられた.
ここではFlaskを用いたサンプルWebアプリに対して,noseを用いたユニットテストを行う方法を書こうと思う.
Flaskを用いたサンプルWebアプリの準備
この記事で作成したFlaskベースのWebアプリに対してnoseを用いたユニットテストを行っていくので,サンプルプログラムをあらかじめ用意しておく.おそらく以下のようなツリー構造になるはず.
- HelloFlask/
- FlaskApp/
- app.py
- static/
- css/
- sample.css
- js/
- sample.js
- templates/
- index.html
- login.html
noseを用いたユニットテスト
noseのインストール
以下のコマンドを実行し,noseをインストールする.
# pip install nose
テストの作成
はじめにテストプログラムを配置するためのディレクトリをTests
という名称でHelloFlask/
の下に作成する.
- HelloFlask/
- FlaskApp/
- app.py
- static/
- css/
- sample.css
- js/
- sample.js
- templates/
- index.html
- login.html
- Tests/
これから書いていくテストコードはすべてTests/
の下に置いていく.
さて,まずは以下のようなテストコードをtest_access.py
というファイル名で作成する.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from nose.tools import eq_, ok_
from FlaskApp import app
app.testing = True
client = app.app.test_client()
def test_get_index():
res = client.get('/')
eq_(302, res.status_code)
ok_('/login' in res.headers['Location'])
app.testing = True
の行でFlaskをテストモードを実行するように設定し,client = app.app.test_client()
の行でFlaskで用意されているテスト用クライアントを取得している.テストの中身はdef test_get_index():
の部分になるのだが,ここではテスト用クライアントで/
,つまりhttp://127.0.0.1:8080/
にアクセスしてその結果を受け取り,その下のeq_
行とok_
行でそれぞれ期待した結果を得られているかをテストしている.今回テスト対象として用いているサンプルアプリでは,/login
ページでの認証がなされない状態で/
にアクセスすると/login
にリダイレクトさせるように実装している.そのためテストコードでは/
にアクセスした際にステータスコードでリダイレクトを示す302
が返ってきているか,リダイレクト先のURLとして/login
を含むURLが指定されているかを確認している.
テストコードを実装できたらプロジェクトのルートディレクトリ,つまりHelloFlask/
の下に移動し,そこでnosetests
コマンドを実行してみよう.すると以下のような出力が得られるはずだ.
.
----------------------------------------------------------------------
Ran 1 test in 0.080s
OK
nosetests
コマンドを実行すると,カレントディレクトリ以下からtest
やTest
を含むファイルを探してそれをテストプログラムとして認識し,プログラム内でtest
やTest
から始まっているメソッドをテストコードとして実行する.上の出力ではテストコードを一つ実行し,テストに成功したと結果を伝えている.
次に,わざと失敗するテストコードを書いてみよう.先ほど作成したtest_access.py
に新たに以下のコードを追加する.
def test_fail_get_index():
res = client.get('/')
eq_(200, res.status_code)
コードを追加できたら,プロジェクトのルートディレクトリに移動して改めてnosetests
を実行してみよう.先ほどとは違った出力が得られるはずだ.FAIL: test_access.test_fail_get_index
という出力,Traceback,AssertionError: 200 != 302
という出力,そしてFAILED (failures=1)
という出力があるはず.これら出力の意味するところはtest_access.py
のtest_fail_get_index
メソッドについて,Tracebackで表示された部分でAssertionError: 200 != 302
があったということである.
その上のメソッドを実装する際に説明した通り,/login
での認証が行えていないため,/
にアクセスしても/login
にリダイレクトされる.そのためステータスコードは200
ではなく302
が返るのでeq_(200, res.status_code)
は成立せず,AssertionError
になるといった具合である.結果を得られたら,この失敗するテストコードは削除しておこう.
削除できたら,test_access.py
を以下のように編集しよう.
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from nose.tools import eq_, ok_
from FlaskApp import app
import json
app.testing = True
client = app.app.test_client()
def test_get_index():
res = client.get('/')
eq_(302, res.status_code)
ok_('/login' in res.headers['Location'])
def test_get_login():
res = client.get('/login')
eq_(200, res.status_code)
def test_fail_login():
res = client.post('/login', data={
'username': 'testuser',
'password': 'testpassword'
})
eq_(200, res.status_code)
def test_login():
res = client.post('/login', data={
'username': 'admin',
'password': 'testpassword'
})
eq_(302, res.status_code)
def test_post_hoge():
res = client.post('/postText',
data=json.dumps(dict(text='hoge')),
content_type='application/json')
eq_(200, res.status_code)
data = json.loads(json.loads(res.data.decode('utf-8'))['ResultSet'])
eq_('hoge', data['result'])
def test_post_HOGE():
res = client.post('/postText',
data=json.dumps(dict(text='HOGE')),
content_type='application/json')
eq_(200, res.status_code)
data = json.loads(json.loads(res.data.decode('utf-8'))['ResultSet'])
eq_('hoge', data['result'])
def test_post_ping():
res = client.post('/postText',
data=json.dumps(dict(text='ping')),
content_type='application/json')
eq_(200, res.status_code)
data = json.loads(json.loads(res.data.decode('utf-8'))['ResultSet'])
eq_('pong', data['result'])
def test_logout():
res = client.get('/logout')
eq_(302, res.status_code)
このテストコードにより,認証の機能,テキスト送信・小文字変換・ping-pong機能,ログアウト処理がうまく動いているかを確認している.テストを行う場合は,先ほどまでと同じくプロジェクトのルートディレクトリに移動し,nosetests
を実行すればいい.テストコードの内容については,ぱっと見で大体理解できるのではないかと思う.また,noseを用いたユニットテストの実装がいかに簡単かがわかるだろう.
このサンプルアプリはGitHubに公開している.
Comments