多多色-多人伦交性欧美在线观看-多人伦精品一区二区三区视频-多色视频-免费黄色视屏网站-免费黄色在线

國內最全IT社區平臺 聯系我們 | 收藏本站
阿里云優惠2
您當前位置:首頁 > php開源 > php教程 > 測試

測試

來源:程序員人生   發布時間:2016-09-30 10:12:32 閱讀次數:2628次

編寫測試單元的目的主要有兩個,實現新功能時,單元測試能夠確保新添加的代碼按預期方式運行,這個進程也可手動完成,不過自動化測試明顯能有效節省時間和精力

另外一重要目的是每次修改程序后,運行單元測試能保證現有代碼的功能沒有退化, 也就是說改動沒有影響原有代碼的正常運行

在最開始,單元測試就是Flasky開發的1部份,我們為數據庫模型類中實現的程序功能編寫了測試,模型類很容易在運行中的程序上下文以外進行測試,因此不用花費太多精力,為數據庫模型中是瞎玩呢的全部功能編寫測試,這最少能有效保證程序這部份在不斷完善的進程中仍能按預期運行

獲得代碼覆蓋報告

編寫測試組件很重要,但知道測試的好壞一樣重要,代碼覆蓋工具用來統計單元測試檢查了多少程序功能,并提供1個詳細的報告,說明程序的哪些代碼沒有測試到,這個信息非常重要,由于它能指引你為最需要測試的部份編寫出新測試

Python提供了1個優秀的代碼覆蓋工具coverage,可使用pip安裝

這個工具本身是1個命令行腳本,可以在任何1個Python程序中檢查代碼覆蓋,除此以外它還提供了更方便的腳本訪問功能,使用編程方式啟動覆蓋檢查引擎,為了能更好地把覆蓋監測集成到啟動腳本manage.py中,我們可以增強之前我們自定義的test命令,添加可選選項--coverage,這個選項的實現方式以下:

import os COV = None if os.environ.get('FLASK_COVERAGE'): import coverage COV = coverage.coverage(branch=True, include='app/*') COV.start() #... @manager.command def test(coveage=False): '''Run the unit tests.''' if coverage and not os.environ.get('FLASK_COVERAGE'): import sys os.environ['FLASK_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 Summary:') COV.report() basedir = os.path.abspath(os.path.dirname(__file__)) covdir = os.path.join(basedir, 'tmp/coverage') COV.html_report(directory=covdir) print('HTML version: file://%s/index.html' % covdir) #...

在Flask-Script中,自定義命令很簡單,若想為test命令添加1個布爾值選項,只需在test()函數中添加1個布爾值參數便可,Flask-Script根據參數名肯定選項名,并據此向函數中傳入TrueFalse

不過,把代碼覆蓋集成到manage.py腳本中有個小問題,test()函數收到 --coverage選項的值后再啟動覆蓋測試已晚了,那時全局作用域中的所有代碼都已履行了,為了檢測的準確性,設定完環境變量FLASK_COVERAGE后,腳本會重啟,再次運行時,腳本頂真個代碼發現已設定了環境變量,因而立即啟動覆蓋檢測

函數coverage.coverage()用于啟動覆蓋測試引擎,branch=True選項開啟分支覆蓋分析,除跟蹤哪行代碼已履行外,還要檢查每一個條件語句的True分支和False分支是不是都履行了,include選項用來限制程序包中文件的分析范圍,只對這些文件中的代碼進行覆蓋檢測,如果不指定include選項,虛擬環境中安裝的全部擴大和測試代碼會包括進覆蓋報告中,給報告添加很多雜項

履行完所有測試后,test()函數會在終端輸出報告,同時還會生成1個使用HTML編寫的精美報告并寫入硬盤,HTML格式的報告非常合適直觀形象地展現覆蓋信息,由于它依照源碼的使用情況給代碼行加上了不同的色彩

# (env) PS C:\Users\Bangys\AppData\Local\GitHub\flasky> python manage.py test test_app_exists (test_basics.BasicsTestCase) ... ok test_app_is_testing (test_basics.BasicsTestCase) ... ok test_home_page (test_client.FlaskClientTestCase) ... ok test_register_and_login (test_client.FlaskClientTestCase) ... ok test_anonymous_user (test_user_model.UserModelTestCase) ... ok test_duplicate_email_change_token (test_user_model.UserModelTestCase) ... ok test_expired_confirmation_token (test_user_model.UserModelTestCase) ... ok test_follows (test_user_model.UserModelTestCase) ... ok test_gravatar (test_user_model.UserModelTestCase) ... ok test_invalid_confirmation_token (test_user_model.UserModelTestCase) ... ok test_invalid_email_change_token (test_user_model.UserModelTestCase) ... ok test_invalid_reset_token (test_user_model.UserModelTestCase) ... ok test_no_password_getter (test_user_model.UserModelTestCase) ... ok test_password_salts_are_random (test_user_model.UserModelTestCase) ... ok test_password_setter (test_user_model.UserModelTestCase) ... ok test_password_verification (test_user_model.UserModelTestCase) ... ok test_ping (test_user_model.UserModelTestCase) ... ok test_roles_and_permissions (test_user_model.UserModelTestCase) ... ok test_timestamps (test_user_model.UserModelTestCase) ... ok test_to_json (test_user_model.UserModelTestCase) ... ok test_valid_confirmation_token (test_user_model.UserModelTestCase) ... ok test_valid_email_change_token (test_user_model.UserModelTestCase) ... ok test_valid_reset_token (test_user_model.UserModelTestCase) ... ok ---------------------------------------------------------------------- Ran 23 tests in 10.205s OK Coverage Summary: Name Stmts Miss Branch BrPart Cover ------------------------------------------------------------------------- app\__init__.py 33 0 0 0 100% app\api_1_0\__init__.py 3 0 0 0 100% app\api_1_0\authentication.py 30 19 10 0 28% app\api_1_0\comments.py 40 30 8 0 21% app\api_1_0\decorators.py 11 3 2 0 62% app\api_1_0\errors.py 17 10 0 0 41% app\api_1_0\posts.py 35 23 6 0 29% app\api_1_0\users.py 30 24 8 0 16% app\auth\__init__.py 3 0 0 0 100% app\auth\forms.py 45 6 8 2 77% app\auth\views.py 109 56 40 6 42% app\decorators.py 14 3 2 0 69% app\email.py 15 0 0 0 100% app\exceptions.py 2 0 0 0 100% app\main\__init__.py 6 0 0 0 100% app\main\errors.py 20 15 6 0 19% app\main\forms.py 39 7 4 0 74% app\main\views.py 169 120 30 2 27% app\models.py 243 59 40 5 73% ------------------------------------------------------------------------- TOTAL 864 375 164 15 51% HTML version: file://C:\Users\Bangys\AppData\Local\GitHub\flasky\tmp/coverage/index.html

上述報告顯示,整體覆蓋率為51%,情況其實不糟,但也不太好,現階段,模型類是單元測試的關注焦點,它包括243個語句,測試覆蓋了其中72%的語句,很明顯,main和auth藍本中的views.py文件和api_1_0藍本中的路由的覆蓋率都很低,所以我們沒有為這些代碼編寫單元測試

有了這個報告,我們就可以很容易肯定向測試組件中添加哪些測試以提高覆蓋率,但遺憾的是,并不是程序的所有組成部份都能像數據庫模型那樣易于測試,所以我們要學習如何去測試視圖函數,表單和模板

注意,由于排版,實例報告中省略了“Missing”列的內容,這1列顯示測試沒有覆蓋的源碼行,是1個由行號范圍組成的長列表

Flask測試客戶端

程序的某些代碼嚴重依賴運行中的程序所創建的環境,例如不能直接調用視圖函數中的代碼進行測試,由于這個函數可能需要訪問Flask上下文全局變量,如requestsession,視圖函數還可能等待接受POST要求中的表單數據,而且某些視圖函數要求用戶先登錄,簡言之,視圖函數只能在要求上下文和運行的程序中運行

Flask內建了1個測試客戶端用于解決(部份解決)這1問題,測試客戶端能復現程序運行在Web服務器中的環境,讓測試扮演成客戶端從而發送要求

在測試客戶端中運行的視圖函數和正常情況下的沒有太大區分,服務器收到要求,將其分配給適當的視圖函數,視圖函數生成響應,將其返回給測試客戶端,履行視圖函數后,生成的響應會傳入測試,檢查是不是正確

測試Web程序

下例是1個使用測試客戶端編寫的單元測試框架

#tests/test_client.py import unittest from app import create_app, db from app.models import User, Role class FlaskClientTestCase(unittest.TestCase): def setUp(self): self.app = create_app('testing') self.app_context = self.app.app_context() self.app_context.push() db.create_all() Role.insert_roles() self.client = self.app.test_client(use_cookies=True) 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('Stranger' in response.get_data(as_text=True))

測試用例中的實例變量self.clinet是Flask測試客戶端對象,在這個對象上可調用方法向程序發起要求,如果創建測試客戶端時啟用了use_cookies選項,這個測試客戶端就可以像閱讀器1樣接受和發送cookie,因此能使用依賴cookie的功能記住要求之間的上下文,值得1提的是,這個選項可用來啟用用戶會話,讓用戶登錄和退出

test_home_page()測試作為1個簡單的例子演示了測試客戶真個作用,在這個例子中,客戶端向首頁發起了1個要求,在測試客戶端上調用get()方法得到的結果是1個Response對象,內容是用視圖函數得到的響應,為了檢查測試是不是成功,要在響應主體中搜索是不是包括‘Stranger’這個詞,響應主體可以使用response.get_data()獲得,而‘Stranger’這個詞包括在向向名用戶顯示的歡迎消息是“Hello,Stranger”中,注意的是,默許情況下get_data()得到的響應主體是1個字節數組,傳入參數as_text=True后得到的是1個更容易于處理的Unicode字符串

測試客戶端還能使用post()方法發送包括表單數據的POST要求,不過提交表單時會有1個小麻煩,Flask-WTF生成的表單中包括1個隱藏字段,其內容是CSRF令牌,需要和表單中的數據1起提交,為了復現這個功能,測試必須要求包括表單的頁面,然后解析響應返回的HTML代碼并提取令牌,這樣才能把令牌和表單中的數據1起發送,為了不在測試中處理CSRF令牌這1繁瑣操作,最好在測試配置中禁用CSRF保護功能,實現方法以下:

# config.py class TestingConfig(Config): #... WTF_CSRF_ENABLED = False

下面是1個更高級的單元測試,摹擬了新用戶注冊賬戶、登錄、使用令牌確認賬戶和退出的進程

# test/text_client.py class FlaskClientTestCase(unittest.TestCase): def text_register_and_login(self): # new account response = self.clinet.post(url_for('auth.register'), data={ 'email':'john@example.com', 'username':'john', 'password':'cat', 'passwprd2':'cat' }) self.assertTrue(response.status_code == 302) # use new account login response = self.clinet.post(url_for('auth.login'), data={ 'email':'john@example.com', 'password':'cat' }m follow_redirects=True) data = response.get_data(as_text=True) self.assertTrue(re.search('Hello, \s+john', data)) self.assertTrue('You have not confirmed your account yet' in data) # send confirm token user = User.query.filter_by(email = 'john@example.com').first() token = user.generate_confirmation_token() response = self.client.get(url_for('auth.comfirm', token=token), follow_redirects=True) data = response.get_data(as_text=True) self.assertTrue('You have comfirmed your account' in data) #quit response = self.client.get(url_for('auth.logout'), follow_redirects=True) data = response.get_data(as_text=True) self.assertTrue('You have been logged out' in data)

這個測試先向注冊路由提交1個表單,post()方法的data參數是個字典,包括表單中的各個字段,各字段的名字必須嚴格匹配定義表單時使用的名字,由于CSRF保護已在測試配置中禁用了,因此無需和表單數據1起發送

/auth/register路由有兩種響應方式,如果注冊數據可用,會返回1個重定向,把用戶轉到登錄頁面,注冊不可用的情況下,返回的響應會再次渲染注冊表單,而且還包括適當的毛病信息,為了確認注冊成功,測試會檢查響應的狀態碼是不是為302,這個代碼表示重定向

這個測試的第2部份使用剛才注冊時使用的電子郵件和密碼登錄程序,這1工作通過向/auth/login路由發起POST要求完成,這1次,調用post()方法時指定了參數follow_redirects=True,讓測試客戶端和閱讀器1樣,自動向重定向的URL發起GET要求,指定這個參數后,返回的不是302狀態碼,而是要求重定向的URL返回的響應

成功登錄后的響應應當是1個頁面,顯示1個包括用戶名的歡迎消息,并提示用戶需要進行賬戶確認才能取得權限,為此,兩個斷言語句被用于檢查響應是不是為這個頁面,值得注意的是,直接搜索字符串“Hello,john!”并沒有用,由于這個字符串由動態部份和靜態部份組成,而且兩部份之間有額外的空白,為了不測試時空白引發的問題,我們使用更加靈活的正則表達式

下1步我們要確認賬戶,這里也有1個小障礙,在注冊進程中,通過電子郵件將確認URL發給用戶,而在測試中處理電子郵件不是1件簡單的事情,上面這個測試使用的解決方法是疏忽了注冊時生成的令牌,直接在User實例上調用方法重新生成1個新令牌,在測試環境中,Flask-Mail會保存郵件正文,所以還有1種可行的解決方法,即通過解析郵件正文來提取令牌

得到令牌后,測試的第3部份摹擬用戶點擊確認令牌URL,這1進程通過向確認URL發起GET要求并附上確認令牌來完成,這個要求的響應是重定向,轉到首頁,但這里再次指定了參數follow_redirects=True,所以測試客戶端會自動向重定向的頁面發起要求,另外,還要檢查響應中是不是包括歡迎消息和1個向用戶說明確認成功的Flash消息

這個測試的最后1步是向退前途由發送GET要求,為了證實確認退出,這段測試在響應中搜索1個Flash消息

測試Web服務

Flask客戶端還可用來測試REST Web服務,下例包括了兩個測試:

def get_api_headers(self, username, password): return { 'Authorization': 'Basic ' + b64encode( (username + ':' + password).encode('utf⑻')).decode('utf⑻'), 'Accept':'application/json', 'Content-Type':'application/json' } def test_no_auth(self): response = self.client.get(url_for('api.get_posts'), content_Type='application/json') self.assertTrue(response.status_code == 401) def test_posts(self): # add a user r = Role.query.filter_by(name="User").first() self.assertIsNotNone(r) u = User(email='john@example.com', password='cat', confirmed=True, role=r) db.session.add(u) db.session.commit() #write a post response = self.clinet.post( url_for('api.new_post'), header=self.get_auth_header('john@example.com', 'cat'), data=json.dumps({'body': 'body of the *blog* post'})) self.assertTrue(response.status_code == 201) url = response.headers.get('Location') self.assertIsNotNone(url) #receive post response = self.client.get( url, headers=self.get_auth_header('john@example.com', 'cat')) self.assertTrue(response_status_code == 200) json_response = json.loads(response.data.decode('utf⑻')) self.assertTrue(json_response['url'] == url) self.assertTrue(json_response['body'] == 'body of the *blog* post') self.assertTrue(json_response['body_html'] == '<p>body of the <em>blog</em> post</p>')

測試API時使用的setUp()tearDown()方法和測試普通程序所用的1樣,不過API不使用cookie,所以無需配置相應支持,get_api_headers是1個輔助方法,返回所有要求都要發送的通用首部,其中包括認證密令和MIME類型相干的首部,大多數測試都要發送這些首部

test_no_auth()是1個簡單的測試,確保Web服務會謝絕沒有提供認證密令的要求,返回401毛病碼,test_posts()測試把1個用戶插入數據庫,然后使用基于REST的API創建1篇博客文章,然后再讀取這篇文章,所有要求主體中發送的數據都要使用json.dumps()方法進行編碼,由于Flask測試客戶端不會自動編碼JSON格式數據,類似的,返回的響應主體也是JSON格式,處理之前必須使用json.loads()進行編碼

生活不易,碼農辛苦
如果您覺得本網站對您的學習有所幫助,可以手機掃描二維碼進行捐贈
程序員人生
------分隔線----------------------------
分享到:
------分隔線----------------------------
關閉
程序員人生
主站蜘蛛池模板: 国产精品人人视频 | 国产在线精品一区二区中文 | 中文字幕一区二区三区在线播放 | 国产欧美日韩一区二区三区 | 黄色的网站免费观看 | 亚洲一本之道在线观看不卡 | 精品国产片 | 高清欧美性猛交xxxx黑人猛交 | 亚洲视频影院 | 亚欧中文字幕 | 亚洲 欧美 国产 制服 动漫 | 欧美另类在线观看 | 国产国产精品人在线观看 | 91精品国产一区二区三区左线 | 91福利在线免费观看 | 福利片午夜 | 国产精品永久免费视频 | 亚洲精品成人在线 | www色com| 午夜影院官网 | 最近最新中文字幕大全2019免费视频 | 久久久久国产精品免费 | 日韩欧美精品在线观看 | 最新日韩精品 | www.亚洲视频.com | 最近免费中文字幕大全高清mv | 国产精品嫩草影院在线 | 波多野结衣免费线在线 | 国产成人精品一区二区三在线观看 | 国产 福利 在线 | 亚洲伊人久久大香线蕉综合图片 | 乱在线伦视频免费 | 欧美freesex10一|3 | 欧美一级毛片免费观看 | 亚洲图片偷拍自拍 | 国产日韩欧美一区二区三区视频 | 久久精品中文字幕不卡一二区 | 极品丝袜高跟91极品系列 | 欧美精品首页 | 亚洲福利一区 | 91久久大香伊蕉在人线 |