flaskで個人ブログを開発する(40)--Flaskの3つのテスト方法
8119 ワード
一、ユニットテスト及び統計テストのカバー率
1.1 Flaskでのユニットテスト
前の記事ではflaskでユニットテストを行う方法を紹介しましたが、現在、私たちのコードには次のユニットテストがあります.classBasicTestCase(unittest.TestCase):
def Setup(self):
self.app=create_app('testing')
self.app_context=self.app.app_context()
self.app_context.push()
db.create_all()
def teardown(self):
db.session.remove()
db.drop_all()
self.app_context.pop()
def test_app_exits(self):
self.assertFalse(current_app is None)
def test_app_is_testing(self):
self.assertFalse(current_app.config['TESTING'])
さらに、managerのコマンドラインにユニットテストを実行するコマンドを追加しました.@manager.command
def test():
"""Run the unittests"""
import unittest
tests=unittest.TestLoader().discover('test')
unittest.TextTestRunner(verbosity=2).run(tests)
コマンドラインを使用して、次の方法でユニットテストを行うことができます.(flasky)hyman@hyman-VirtualBox:~/Github/flaskTs$ python manager.py test
test_app_exits(test_basic.BasicTestCase) ... ok
test_app_is_testing(test_basic.BasicTestCase) ... ok
----------------------------------------------------------------------
Ran 2 tests in0.005s
OK
1.2 coverageによるユニットテストカバー率の統計
Flaskでは、coverageモジュールを使用してユニットテストを行うコードオーバーライド率を統計できます.そのインストール方法は次のとおりです.pip installcoverage
インストールが完了したら、coverageをカスタムコマンドのオプションとしてtestコマンドに追加します.COV=None
ifos.environ.get('COVERAGE'):
import coverage
COV=coverage.coverage(branch=True,include='*')
COV.start()
@manager.command
deftest(coverage=False):
"""Run the unittests"""
if coverage and notos.environ.get('COVERAGE'):
import sys
os.environ['COVERAGE']='1'
os.execvp(sys.executable,[sys.executable]+sys.argv)
import unittest
tests=unittest.TestLoader().discover('tests')
unittest.TextTestRunner(verbosity=2).run(tests)
if COV:
COV.stop()
COV.save()
print('Coverage:')
COV.report()
COV.erase()
まず、グローバルな範囲で、「COVERAGE」環境変数があるかどうかを確認し、定義されている場合は、coverageのcoverage関数を使用してcoverageオブジェクトCOVを構築します.coverage関数には2つのパラメータがあり、1つ目のパラメータbranchがTrueに設定されていることは、チェックのモジュールが指定されたディレクトリの下のサブディレクトリを含むことを示し、2つ目のパラメータincludeは指定されたディレクトリで、'*'に設定されていることは、現在のディレクトリと現在のディレクトリの下のモジュールビットチェックモジュールを設定することを意味します.
一方、test()関数では、追加のコマンドラインパラメータcoverageがtest()関数のboolタイプのパラメータとなり、コマンドラインにパラメータが追加されるとcoverageはTrueに等しくなります.関数内部では、coverageが「COVERAGE」環境変数を定義しているかどうかを判断します.COVを定義実行するコードはグローバルコードであり、testコマンドラインを実行する前に実行するため、判断が完了した後にosを呼び出す.execvpはプログラムを再実行しました.次にCOV変数はNoneではなくif COVの後の操作を行うことができる.
実行したプログラムの結果は,現在のディレクトリの下およびサブディレクトリの下のすべてのモジュールを統計対象としているため,flask自体が持参したモジュールの一部が含まれている.
二、Flask Webテストクライアントを使用してテストする
Flaskのメンバー関数test_Client()は、Webクライアントをシミュレートして、Webサービス側と対話するためのクライアントオブジェクトを返すことができます.Webプログラムをテストしたり、Rest APIをテストしたりすることができます.次に、クライアントの使用について説明します.
2.1 Webプログラムのテスト
新しいFlaskClientTestユニットのテストクラスは以下の通りです.classFlaskClientTest(unittest.TestCase):
def setUp(self):
self.app=create_app('testing')
self.app_context=self.app.app_context()
self.app_context.push()
db.create_all()
self.client=self.app.test_client()
def tearDown(self):
db.session.remove()
db.drop_all()
self.app_context.pop()
def test_home_page(self):
response=self.client.get(url_for('main.index'))
self.assertTrue('Home' inresponse.get_data(as_text=True))
def test_register(self):
response=self.client.post(url_for('main.register'),data={
'email':'[email protected]',
'name':'Hyman',
'password1':'123',
'password2':'123'})
self.assertTrue(response.status_code==302)
test_home_pageは、テストを行うためのホームページのテスト例であり、ここではselfを用いる.client.get(url_for('main.index'))のメソッドは、GETメソッドを使用して対応するurlにアクセスし、応答結果を取得します.そしてresponseのget_を呼び出すdata()は応答結果を取得し、ここでas_を設定するtext=Trueは、結果を文字列にフォーマットし、「Home」がこの文字列にあるかどうかを判断します.
test_registerは、登録ユーザプロセスをシミュレートし、登録されたルーティングをテストするために使用されます.ここではPOSTの方法でフォームを送信しましたが、dataではフォームの各フィールドに対する付与値であり、このkey値は必ず私たちが定義した登録フォームのフィールド名と一致しなければなりません.登録ルーティングは最終的にホームページにリダイレクトされるため、サービス側はブラウザ302に返されるステータスコードに戻り、応答のstatus_を判断するcodeが302であるか否かで試験に合格したか否かを判断する.
2.2 Rest APIのテスト
次に、前回の記事で作成した新しいブログを追加するためのRest APIをテストします.deftest_posts(self):
response=self.client.post(
url_for('main.new_post'),
data=json.dumps({'body':'I am a newpost'}),
content_type='application/json')
self.assertTrue(response.status_code==200)
ここで注意しなければならないのは、Rest APIをテストする際にclientのpostメソッドに表示される指定転送のデータ型content_typeは「アプリケーション/json」であり、転送するdataはjsonを用いる必要がある.dumps()メソッドは、データをjsonのフォーマットにフォーマットします.assertTrueで応答を検証するstatus_codeは,文書作成に成功すると,サービス側が200のステータスコードを返すので,ここでは200と比較した.
三、seleniumによる自動化テスト
Seleniumは自動的にブラウザを起動し、ユーザーがブラウザをクリックする接続をシミュレートすることができ、seleniumのインストール方法は以下の通りである.pip install selenium
テストの考え方では,バックグラウンドスレッドを用いてサービス側を起動し,メインスレッドでseleniumを用いてfirefoxを自動的に操作して自動化したテストを行う.seleniumを使用する前に、テスト終了後にサービス側が自動的に実行を終了するように、サービス側の実行を閉じるルーティングを定義します.@main.route('/shutdown')
defserv_shutdown():
shutdown=request.environ.get('werkzeug.server.shutdown')
shutdown()
return 'shuting down'
werkzeugサーバはshutdown関数を外部で使用するために提供しており、上記のビュー関数では、この関数を取得して呼び出し、アクセスすることでhttp://localhost:5000/shutdownこのurlでは、サービス側を閉じることができます.
次にseleniumを使用して自動化テストを行うコードです.#coding:utf-8
import unittest
from flaskimport current_app,url_for,json
from app importcreate_app,db
from seleniumimport webdriver
classSeleniumTestCase(unittest.TestCase):
client=None
@classmethod
def setUpClass(cls):
# firefox
try:
cls.client=webdriver.Firefox()
except:
pass
if cls.client:
cls.app=create_app('testing')
cls.app_context=cls.app.app_context()
cls.app_context.push()
db.create_all()
threading.Thread(target=cls.app.run).start()
@classmethod
def tearDownClass(cls):
if cls.client:
cls.client.get('http://localhost:5000/shutdown')
cls.client.close()
db.drop_all()
db.session.remove()
cls.app_context.pop()
def setUp(self):
if not self.client:
self.skipTest('Firefox isinvailable')
def tearDown(self):
pass
def test_home_page(self):
self.client.get('http://localhost:5000/')
self.assertTrue(re.search('Home',self.client.page_source))
修飾子classmethodによってsetupClass()とtearDownClass()の2つのメンバー関数が定義され、クラス内のすべてのテスト・インスタンスが実行される前と後に実行され、setuUp()とtearDown()は各テスト・インスタンスが実行される前後に実行されます.setUpClass()でwebdriver.Firefox()は、Firefox()ブラウザオブジェクトを取得し、threading.Thread(target=cls.app.run).start()でバックグラウンドスレッドでサービス側を実行し、テスト例test_home_pageではこのブラウザオブジェクトからアクセスします.http://localhost:5000/'を使用して、返された結果に'Home'文字列が含まれているかどうかを正規表現で判断します.すべてのテストが完了したら、tearDownClass()にアクセスします.http://localhost:5000/shutdown'サービス側のクローズが完了しました.これで、完全なseleniumテストプロセスが完了しました.
Seleniumの強みは、ブラウザを操作して自動化されたテストを実現できることです.ブラウザでクリックする人がいるように、Firefoxクライアントオブジェクトを作成することで、Webインタフェースで関連する要素を見つけて後続の操作を行う方法がたくさんあります.例えばfind_element_by_link_text()メソッドは、ハイパーリンクの名前に基づいてリンクを見つけ、そのClick()メソッドを呼び出してハイパーリンクをクリックするシミュレーションを行うことができます.find_element_by_name()メソッドは、名前に基づいて対応するフィールドを見つけ、期間send_を使用できます.keys()のメソッドは、各フィールドに値を入力します.
Github位置:https://github.com/HymanLiuTS/flaskTs本プロジェクトのクローン:Git [email protected]:HymanLiuTS/flaskTs.Git本明細書のソースコードを取得する:Git checkout FL 40
classBasicTestCase(unittest.TestCase):
def Setup(self):
self.app=create_app('testing')
self.app_context=self.app.app_context()
self.app_context.push()
db.create_all()
def teardown(self):
db.session.remove()
db.drop_all()
self.app_context.pop()
def test_app_exits(self):
self.assertFalse(current_app is None)
def test_app_is_testing(self):
self.assertFalse(current_app.config['TESTING'])
@manager.command
def test():
"""Run the unittests"""
import unittest
tests=unittest.TestLoader().discover('test')
unittest.TextTestRunner(verbosity=2).run(tests)
(flasky)hyman@hyman-VirtualBox:~/Github/flaskTs$ python manager.py test
test_app_exits(test_basic.BasicTestCase) ... ok
test_app_is_testing(test_basic.BasicTestCase) ... ok
----------------------------------------------------------------------
Ran 2 tests in0.005s
OK
pip installcoverage
COV=None
ifos.environ.get('COVERAGE'):
import coverage
COV=coverage.coverage(branch=True,include='*')
COV.start()
@manager.command
deftest(coverage=False):
"""Run the unittests"""
if coverage and notos.environ.get('COVERAGE'):
import sys
os.environ['COVERAGE']='1'
os.execvp(sys.executable,[sys.executable]+sys.argv)
import unittest
tests=unittest.TestLoader().discover('tests')
unittest.TextTestRunner(verbosity=2).run(tests)
if COV:
COV.stop()
COV.save()
print('Coverage:')
COV.report()
COV.erase()
Flaskのメンバー関数test_Client()は、Webクライアントをシミュレートして、Webサービス側と対話するためのクライアントオブジェクトを返すことができます.Webプログラムをテストしたり、Rest APIをテストしたりすることができます.次に、クライアントの使用について説明します.
2.1 Webプログラムのテスト
新しいFlaskClientTestユニットのテストクラスは以下の通りです.
classFlaskClientTest(unittest.TestCase):
def setUp(self):
self.app=create_app('testing')
self.app_context=self.app.app_context()
self.app_context.push()
db.create_all()
self.client=self.app.test_client()
def tearDown(self):
db.session.remove()
db.drop_all()
self.app_context.pop()
def test_home_page(self):
response=self.client.get(url_for('main.index'))
self.assertTrue('Home' inresponse.get_data(as_text=True))
def test_register(self):
response=self.client.post(url_for('main.register'),data={
'email':'[email protected]',
'name':'Hyman',
'password1':'123',
'password2':'123'})
self.assertTrue(response.status_code==302)
test_home_pageは、テストを行うためのホームページのテスト例であり、ここではselfを用いる.client.get(url_for('main.index'))のメソッドは、GETメソッドを使用して対応するurlにアクセスし、応答結果を取得します.そしてresponseのget_を呼び出すdata()は応答結果を取得し、ここでas_を設定するtext=Trueは、結果を文字列にフォーマットし、「Home」がこの文字列にあるかどうかを判断します.
test_registerは、登録ユーザプロセスをシミュレートし、登録されたルーティングをテストするために使用されます.ここではPOSTの方法でフォームを送信しましたが、dataではフォームの各フィールドに対する付与値であり、このkey値は必ず私たちが定義した登録フォームのフィールド名と一致しなければなりません.登録ルーティングは最終的にホームページにリダイレクトされるため、サービス側はブラウザ302に返されるステータスコードに戻り、応答のstatus_を判断するcodeが302であるか否かで試験に合格したか否かを判断する.
2.2 Rest APIのテスト
次に、前回の記事で作成した新しいブログを追加するためのRest APIをテストします.
deftest_posts(self):
response=self.client.post(
url_for('main.new_post'),
data=json.dumps({'body':'I am a newpost'}),
content_type='application/json')
self.assertTrue(response.status_code==200)
ここで注意しなければならないのは、Rest APIをテストする際にclientのpostメソッドに表示される指定転送のデータ型content_typeは「アプリケーション/json」であり、転送するdataはjsonを用いる必要がある.dumps()メソッドは、データをjsonのフォーマットにフォーマットします.assertTrueで応答を検証するstatus_codeは,文書作成に成功すると,サービス側が200のステータスコードを返すので,ここでは200と比較した.
三、seleniumによる自動化テスト
Seleniumは自動的にブラウザを起動し、ユーザーがブラウザをクリックする接続をシミュレートすることができ、seleniumのインストール方法は以下の通りである.pip install selenium
テストの考え方では,バックグラウンドスレッドを用いてサービス側を起動し,メインスレッドでseleniumを用いてfirefoxを自動的に操作して自動化したテストを行う.seleniumを使用する前に、テスト終了後にサービス側が自動的に実行を終了するように、サービス側の実行を閉じるルーティングを定義します.@main.route('/shutdown')
defserv_shutdown():
shutdown=request.environ.get('werkzeug.server.shutdown')
shutdown()
return 'shuting down'
werkzeugサーバはshutdown関数を外部で使用するために提供しており、上記のビュー関数では、この関数を取得して呼び出し、アクセスすることでhttp://localhost:5000/shutdownこのurlでは、サービス側を閉じることができます.
次にseleniumを使用して自動化テストを行うコードです.#coding:utf-8
import unittest
from flaskimport current_app,url_for,json
from app importcreate_app,db
from seleniumimport webdriver
classSeleniumTestCase(unittest.TestCase):
client=None
@classmethod
def setUpClass(cls):
# firefox
try:
cls.client=webdriver.Firefox()
except:
pass
if cls.client:
cls.app=create_app('testing')
cls.app_context=cls.app.app_context()
cls.app_context.push()
db.create_all()
threading.Thread(target=cls.app.run).start()
@classmethod
def tearDownClass(cls):
if cls.client:
cls.client.get('http://localhost:5000/shutdown')
cls.client.close()
db.drop_all()
db.session.remove()
cls.app_context.pop()
def setUp(self):
if not self.client:
self.skipTest('Firefox isinvailable')
def tearDown(self):
pass
def test_home_page(self):
self.client.get('http://localhost:5000/')
self.assertTrue(re.search('Home',self.client.page_source))
修飾子classmethodによってsetupClass()とtearDownClass()の2つのメンバー関数が定義され、クラス内のすべてのテスト・インスタンスが実行される前と後に実行され、setuUp()とtearDown()は各テスト・インスタンスが実行される前後に実行されます.setUpClass()でwebdriver.Firefox()は、Firefox()ブラウザオブジェクトを取得し、threading.Thread(target=cls.app.run).start()でバックグラウンドスレッドでサービス側を実行し、テスト例test_home_pageではこのブラウザオブジェクトからアクセスします.http://localhost:5000/'を使用して、返された結果に'Home'文字列が含まれているかどうかを正規表現で判断します.すべてのテストが完了したら、tearDownClass()にアクセスします.http://localhost:5000/shutdown'サービス側のクローズが完了しました.これで、完全なseleniumテストプロセスが完了しました.
Seleniumの強みは、ブラウザを操作して自動化されたテストを実現できることです.ブラウザでクリックする人がいるように、Firefoxクライアントオブジェクトを作成することで、Webインタフェースで関連する要素を見つけて後続の操作を行う方法がたくさんあります.例えばfind_element_by_link_text()メソッドは、ハイパーリンクの名前に基づいてリンクを見つけ、そのClick()メソッドを呼び出してハイパーリンクをクリックするシミュレーションを行うことができます.find_element_by_name()メソッドは、名前に基づいて対応するフィールドを見つけ、期間send_を使用できます.keys()のメソッドは、各フィールドに値を入力します.
Github位置:https://github.com/HymanLiuTS/flaskTs本プロジェクトのクローン:Git [email protected]:HymanLiuTS/flaskTs.Git本明細書のソースコードを取得する:Git checkout FL 40
pip install selenium
@main.route('/shutdown')
defserv_shutdown():
shutdown=request.environ.get('werkzeug.server.shutdown')
shutdown()
return 'shuting down'
#coding:utf-8
import unittest
from flaskimport current_app,url_for,json
from app importcreate_app,db
from seleniumimport webdriver
classSeleniumTestCase(unittest.TestCase):
client=None
@classmethod
def setUpClass(cls):
# firefox
try:
cls.client=webdriver.Firefox()
except:
pass
if cls.client:
cls.app=create_app('testing')
cls.app_context=cls.app.app_context()
cls.app_context.push()
db.create_all()
threading.Thread(target=cls.app.run).start()
@classmethod
def tearDownClass(cls):
if cls.client:
cls.client.get('http://localhost:5000/shutdown')
cls.client.close()
db.drop_all()
db.session.remove()
cls.app_context.pop()
def setUp(self):
if not self.client:
self.skipTest('Firefox isinvailable')
def tearDown(self):
pass
def test_home_page(self):
self.client.get('http://localhost:5000/')
self.assertTrue(re.search('Home',self.client.page_source))