https://en.wikipedia.org/wiki/Cross-origin_resource_sharing
寫在前面
業務驅動,不驅就不動
一.背景
用Selenium來做前端自動化測試,希望一提代碼就來份測試報告:
1.push/merge遠程代碼
2.自動跑測試用例
3.郵件通知測試結果
由Webhook來連接Git操作和構建過程,這樣每次push/merge等Git操作時,Webhook就會(請求)通知構建服務,然後執行整個構建過程,構建完畢後郵件通知
本來Jenkins已經提供一條龍服務了,簡單配置一下就好。Webhook觸發Jenkins job,同步構建完畢後發郵件出來
但由於前端環境的特殊性(測試用例要在瀏覽器環境跑),而測試結果也在JS手裏,於是就有了異步跑完用例後怎麼通知Jenkins的問題
用Selenium起一個瀏覽器,訪問測試頁面,等所有case執行完畢(包括異步case),這時纔拿到測試結果。而構建腳本可能在瀏覽器還沒打開的時候就執行完畢了,準備發郵件了用例還沒跑完
所以先把發郵件的任務拆出來,作爲獨立的job,專門負責發郵件。那麼只缺少JS通知Jenkins發郵件這一環了
二.專門發郵件的job
配置SMTP
在“Jenkins/系統管理/系統設置”裏填寫SMTP相關配置,以及系統管理員郵件地址,例如:
郵件通知
---
SMTP服務器 smtp.163.com
使用SMTP認證
用戶名 xxx
密碼 ***
Jenkins Location
---
系統管理員郵件地址 [email protected]
郵件通知部分底部有通過發送測試郵件測試配置
選項,填寫收件人試一發,能收到就好
P.S.特別注意填寫系統管理員郵件地址,否則永遠發不出去(沒填發件人)
然後配置“Extended E-mail Notification”,構建後郵件通知的內容在這裏設置,例如:
Extended E-mail Notification
---
SMTP server smtp.163.com
Default Content Type HTML(text/html)
Use SMTP Authentication
User Name xxx
Password ***
勾選底部“Enable Debug Mode”方便排查郵件發送失敗原因
參數化構建
在“General/參數化構建過程”裏設置參數名、默認值及描述,例如:
String Parameter
---
名字 autoTestResult
默認值 自動測試失敗
描述 自動測試結果,不通過的用例有哪些
在“構建觸發器/觸發遠程構建 (例如,使用腳本)”裏填寫token,作爲構建口令,例如:
身份驗證令牌 mail
在“構建”裏執行shell(用來測試參數傳遞):
echo $autoTestResult
echo 'hoho, the end'
在“構建後操作”裏添加“Editable Email Notification”,填寫收件人/內容等項,例如:
Project Recipient List [email protected]
Default Subject autoTest通知
Default Content $autoTestResult
然後在“Advanced Settings/Triggers”裏添加“Always”,默認只在構建失敗時發郵件,改爲無腦發
然後嘗試一下,手動觸發(瀏覽器訪問):
http://localhost:2017/job/mail/buildWithParameters?token=mail&cause=shoujian&autoTestResult=allpassed
一切正常的話,本次構建的“Console Output”面板會出現如下log:
Started by remote host 0:0:0:0:0:0:0:1 with note: shoujian
Building in workspace /Users/Shared/Jenkins/Home/jobs/mail/workspace
Checking for pre-build
Executing pre-build step
Checking if email needs to be generated
No emails were triggered.
[workspace] $ /bin/sh -xe /Users/Shared/Jenkins/tmp/hudson5065606977113971836.sh
+ echo allpassed
allpassed
+ echo hoho
hoho
Checking for post-build
Performing post-build step
Checking if email needs to be generated
Email was triggered for: Always
Sending email for trigger: Always
messageContentType = text/html; charset=UTF-8
Collecting change authors...
build: 17
Adding recipients from project recipient list
Adding recipients from trigger recipient list
Successfully created MimeMessage
Sending email to: [email protected]
Finished: SUCCESS
然後將收到一封郵件:
主題:autoTest通知
內容:allpassed
P.S.如果按照上面的步驟一點一點來,應該一切正常,如果報錯或者收不到郵件,請查看Jenkins Email sending fails
三.HTTP觸發Jenkins郵件job
由JS發HTTP請求觸發Jenkins任務,會遇到3個問題:
-
CSRF保護
-
CORS
-
登錄驗證(Basic Auth)
默認有跨站請求頭僞造保護和跨域限制,而且要求登錄(但支持Basic Auth)
如果經服務中轉,這些都不是問題,拿到crumb
通過CSRF,無腦跨域,header
驗證。但考慮構建工具只在測試環境跑,沒必要這麼繞,乾脆關掉CSRF保護,開啓CORS白名單,最後JS可以通過XHR設置header完成Basic
Auth登錄
關掉CSRF保護
在“Jenkins/管理Jenkins/Configure Global Security”裏不勾選防止跨站點請求僞造
這樣就不用取crumb
驗證了,測試環境,風險不大
開啓CORS白名單
有一個CORS插件,專門幹這個事情:CORS support for Jenkins
在“Jenkins/系統管理/系統設置/CORS Filter”裏填寫跨域限制相關響應頭,例如:
Is Enabled
Access-Control-Allow-Origins http://localhost:3000
Access-Control-Allow-Methods POST
Access-Control-Allow-Headers Authorization
Access-Control-Max-Age 3600
P.S.如果值有多個,用,
隔開,例如POST,GET
把POST http://localhost:3000
添進白名單,允許XHR讀取響應頭的Authorization
字段,身份證有效期爲3600s(1小時)
P.S.關於CORS的更多信息,請查看跨域資源共享 CORS 詳解
XHR登錄
new Image()
最方便,但沒辦法進行Basic
Auth。XHR GET也可以,但URL長度限制總不爽,所以選擇XHR POST,示例如下:
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if((xhr.status >=200 && xhr.status < 300 ) || xhr.status == 304 ){
console.info('jenkins觸發成功');
} else {
console.info('jenkins觸發失敗 ' + xhr.status + '\n' + xhr.responseText);
}
}
};
var result = 'allpassed';
var url = 'http://localhost:2017/job/mail/buildWithParameters?token=mail&cause=shoujian&autoTestResult=' + result;
xhr.open('POST', url, true);
// Basic Auth
var username = 'jenkins', password = '2017';
xhr.setRequestHeader('Authorization', 'Basic ' + btoa(username + ':' + password));
xhr.send(null);
測試用例全部執行完畢後,把測試結果POST出去,通知Jenkins發郵件。到這裏前端自動化測試就能跑起來了
P.S.關於XHR Basic Auth的更多信息,請查看Jenkins json REST api with CORS request using jQuery
PHP觸發Jenkins參數化構建
如果考慮安全風險,或者希望在跑完測試用例後做更多的事情,可以由服務觸發構建,PHP代碼如下:
header('Access-Control-Allow-Origin:*');
// 觸發構建
// exec("curl -X GET \"http://127.0.0.1:2017/job/mail/build?token=mail&cause=nocause\" --user user:passwd", $res, $rt);
// 參數化構建
exec("curl -X GET \"http://127.0.0.1:2017/job/mail/buildWithParameters?token=mail&cause=nocause&autoTestResult=allpassed\" --user user:passwd", $res, $rt);
if ($rt !== 0) {
echo '出錯了<br>';
var_dump($res);
}
else {
echo 'ok';
}
exec
調用curl
發送GET請求,POST方式與之類似,不再贅述
P.S.關於遠程觸發構建的更多信息,請查看官方文檔:Remote access API
四.寫在最後
Jenkins搞定了很多細節的工作,比手動實現這樣一套構建服務要方便一些,但可配置度越高,控制權就越低,想要完成一些簡單的操作,不得不求助於插件或者通過一些繞來繞去的方式
不管怎樣,自動跑用例,保證基礎設施穩定性是極好的,搞起來