最近看了不少有關
探索性測試的討論和觀點,老實說越看越糊涂。所以忍不住吐槽一下,在這里和大家討論一下探索性測試。希望對于想
學習和嘗試探索性測試的朋友有所幫助澄清,或者是更加糊涂,^_^。
探索性測試有很多很多的定義:
百度百科的定義:“同時設計測試和執行測試”。 嗯。。什么意思?
Cem 老人家的正式定義:“a style of software testing that emphasizes the personal freedom and responsibility of the individual tester to continually optimize the quality of his/her work by treating test-related learning, test design, test execution, and test result interpretation as mutually supportive activities that run in parallel throughout the project”。啊。。 糊涂了。。。
有人說:“手工測試就是探索性測試”。 更糊涂了。。。
又有人說:“探索性測試就是一邊探索一邊測試”。 徹底糊涂了。。。。
。。。。。
那么探索性測試到底是啥玩意啊?
我們先來看一個例子吧。很多人都玩過猜數字的游戲吧。我心里想一個數字,你來猜。你可以問任何問題,我回答“是”還是“不是”。然后你通過不斷問問題和 我的回答來最終猜到我心想的數字。在猜對的情況下問的問題越少得分越高。比如,我心里想了一個數字。你可以問“大于零?”,我說“是”。你現在知道是正數 了,你然后問“小于100?”,我說“是”。你現在知道是小于100的正數,你然后問“小于50?”,我說“不是”。你現在知道是介于50和100間的 數。你繼續再問幾次后因該就能猜對了。
在這個簡單的游戲中有兩個策略至關重要:
1、你要根據前面問題的答案來分析和設計下一個問題。第一個問題可能不著邊,但是第二個問題會讓你跟接近你想要的答案。第三個會更加靠近,以此類推。
2、僅僅根據前面問題的答案來設計下一個問題可以最終幫你猜對數字,但是要想用最少的問題來猜對數字不僅要根據前面問題的答案,而且需要對問題本身其它 知識加以綜合運用使用其它策略和技術。比如在知道是小于100的正數后,你可以使用binary search,最多猜6次就可以猜對;如果你不知道binary search,你可以猜是否小??90?小于80?小于70?… 猜上十幾次也可以猜對;或者猜是否小于99?小于98?小于97?… 猜上幾十次也可以猜對。所以采用不同策略直接決定你猜對的速度。
所以兩個關鍵因素:前面問題的答案+有效的策略。
探索性測試和猜數字游戲完全一樣。在這里要猜的數字就是你要找的bug。你問的問題就是你做的測試,每個問題的答案就是你測試過程中產品的輸出。第一輪 你只有一個非常模糊的范圍比如測試某個模塊的某個功能。在你測試的時候通過觀察產品的反應和輸出來判斷分析下一步做什么會發現bug。當然實際測試中不會 像猜數字那樣直接和簡單。
下面我們來看一下一個真實的測試例子。有一次我在測試一個用戶界面的錄入頁面。用戶可以輸入比如姓名,年齡,等等很多信息,最后系統根據輸入的內容處理保存到數據庫中。 當然我對每一個輸入框都會嘗試不同的數據比如空值,很長的字符串,空格等等,系統都沒有問題。但是我注意到每次保存的時候系統都會生成一個本地文件,該文 件的名字是其中一個輸入框的我的輸入。該輸入框的唯一限制就是不可以為空不可以超過255個字符。我想到文件名字中不可以有斜杠“\”,于是我就在該輸入 框中如入“ab\cd”,它通過了輸入校驗,但是保存的時候系統就崩潰了。這就是探索性測試一個非常典型的例子,通過觀察分析上一次測試的產品反應和輸出 來判斷系統會有問題的地方,然后設計調整步驟和測試數據反復嘗試直到完全驗證模塊沒有問題或找到bug.
探索性測試和手工測試的區別: 手工測試通常是指完全按照預先設計好的步驟一步一步人工測試直到驗證了所要驗證的功能。如果結果和預期結果一致,則驗證通過;如果不一致,則是bug.可 以看出手工測試過程單調沒有思考沒有變通。在上面的猜數字的游戲中你明明已經知道是正數,你還在按照游戲開始前設計的步驟問大于-100? 大于-90?。。。。當然現實生活中 沒有這樣的傻子,在你“手工”測試的時候你或多或少已經使用“探索性”了,只不過你沒有意識到罷了。所以很多人誤認為探索性測試是個時髦新測試技術,研究 了半天又不知道到底新在那里和自己一致做的有什么不同。或者恍然大悟原來自己已經探索了很多年了。但是探索性測試有效率高和效率低之分,所以有人干脆就把 效率高ET的才叫ET, 效率低的ET叫手工測試。這也是讓人糊涂的原因之一吧。
測試自動化就是把手工測試的每個步驟有測試自動化工具來完成。好處是不用人做了,缺點是測試過程中仍然沒有思考沒有變通。
Ad-hoc測試(隨機測試):沒有預先設計好的步驟,也沒有明確目標,也沒有策略。在上面猜數字的游戲中你明明知道是正數,你還在東一榔頭,西一棒槌的亂猜等于100?等于-100?等于0?。。。當然也有可能被你一不小心蒙對了。
探索性測試和測試自動化各有各的優缺點。至于什么時候開始測試自動化,什么時候開始ET,先測試自動化后ET,還是先ET后測試自動化需要看項目產品具體情況了。沒有絕對對錯,以盡早發現bug,發現更多的的bug為宗旨。另外既然ET和測試自動化的各自優缺點,微軟有 些組最近兩年在嘗試“探索性測試自動化”的方式來把探索性測試和測試自動化相結合,充分發揮各自的優點。看到這里你可能要恨我了,我剛學會測試自動化,你 又提倡ET了;我剛搞清楚ET,你又開始提倡探索性測試自動化了。。。 呵呵,人類發展過程就是通過社會分工,揚長避短。專注于做自己擅長的事情,把自己不擅長做的事情交給擅長的人去做。社會發展是如此,云計算是 如此,測試也是如此。有人說過:“The computer is incredibly fast, accurate, and stupid (test automation). Man is unbelievably slow, inaccurate, and brilliant (exploratory test). The marriage of the two is a force beyond calculation。”
其實我們可以看到探索性測試入門容易或者你已經在做了多年了,難得是有效地探索性測試,或者做效率高的 ET(否則被別人不屑為手工測試了J)。那么如何根據前面的測試結果來分析和思考,如何才能敏銳地嗅覺到通向bug的種種線索?當然有多種方式來訓練自己 這方面的能力。還有如何衡量考核ET的效率,投入和產出比率?欲知詳情,請聽下回分解。。。。
所謂偉大的測試團隊是什么意思? “擁有明星隊員的團隊是好團隊,但沒有一個明星隊員的團隊是一個偉大的團隊”——無名氏。
上述的引言使我們進入了偉大團隊及其特性的討論之中。這篇文章源于作者在不同團隊中的工作經歷,對團隊成員在時間非常緊迫和項目非常復雜的情況下的表現的觀察。本文適合于那些想尋求合適成員以求在項目中有出色表現的軟件測試團隊。
為什么軟件測試團隊有的成功而有的失敗?
這問題有解決方案嗎?答案究竟是“是”還是“否”,取決于成員如何向團隊的共同目標看齊,不應是抑制個人興趣,而是對問題有一致認識而共事。
團隊的成功也依靠帶頭人的領導特質——“船長”
本文目標是幫助軟件測試工程師或是相信團隊合作的人理解優秀團隊的特質,并在自己團隊中培養這些特質。
從長遠的角度看考慮,成功的團隊依靠的不是被認為是“明星”的個人,而是組成這個星群的全體,這樣才能創造出偉大的團隊。
偉大軟件測試團隊的特質初始階段——問自己下列問題:你們團隊的新成員知道自己入選團隊的原因嗎?
團隊新成員常常會為自己在團隊中存在的價值而感到困惑。雖然你可能會說他/她不需要知道目標,只要根據任務安排進行工作就可以了,這是很多高管的想法。 但是清晰明確地定義角色和職責可以幫助個人在更大的背景下理解這個項目。它包括工作相關內容、個人技能以及早期定義的團隊共同目標。這些將會對工作會有巨 大的作用(幫助),因此也會改善其質量。
領導權:隨著任務和團隊規模的增加,項目的復雜性也隨之增加,這時讓一個領導者跟蹤每個人的任 務情況是不太可能的。因此,一個解決辦法就是下放權力給個人。然而,如果沒有經過深思熟慮,這種虛擬的權利經常表現為一種阻礙而非解決方案。僅僅委派給個 人作為負責人,而沒有認真考慮過他/她是否能勝任不會帶來預期的效果。
想表現的像一個負責人,他應該具有和領導者接近的思維方式和行使 自己未來領導者職責的自豪感。這些人會在促進團隊團結一致的過程中發揮重要作用,而那些對團隊漠不關心的人則會毀了團隊。負責人的職責不僅是給團隊成員分 配任務,而要以更開闊的視角認清手上的任務及形勢,給團隊成員帶來共識。當成員處理任務遇到困難時予以支持、鼓勵,以同伴而不是領導的身份糾正成員們的錯 誤,提出建議,在適當時機采納老員工的意見,這些都有利于共同目標的實現。合作以及團隊中牢固的相互依存感可以消除彼此間的相互指責,增加學習與進步的機會。
團隊中專職成員的知識
專職成員是指那些在相同項目或類似工作中投入大量時間的人。他們掌握大量關于項目的知識,是團隊的資源。通過合適的方法激勵出他們的專業知識,從而使整個團隊都會從中受益。這些人應該在其他工作上表現勤奮而非自大。常言說:“往昔成會滋生自滿”。他們是重要的成員,少了他們可能會影響到整個團隊,但這不是唯一的準則。因為這也給了其他有相同能力的人在這個位子上表現的機會。
激勵——關鍵因素激勵不總是把大家聚集起來發表演說,而是抓住每一次機會將這些話單獨說給每個成員。每個成員都有獨特的個性和工作方式。對Test Leader來說,這個工作要比“說”來的復雜,因為它要求領導者不僅從分配任務的角度而且從整個項目的角度,去體會每個成員的想法。積極正向的領導態度將會給團隊注入強大的活力-- 這一點來源于在偉大測試團隊中的工作經驗。如果領導者抱怨工作時間過長或堅持要求團隊成員按無法實現的計劃行事,你的態度也會反映在你們的團隊中。真正的 領導者應該是這樣的,盡管計劃不合理,還是要給團隊成員注入信心,讓他們相信自己的能力。同時,在背后為團隊為這個計劃所做的努力做出解釋和辯護,而不是 推遲計劃使得成員工作變得簡單。
認可
每個人都希望自己的工作得到認可。當一個人 因為工作受到獎勵,領導者就有責任告訴其他人獲獎原因。在這個問題上,領導者的決定必須是公正的。它會使受到獎勵的人得到團隊成員的尊重。團隊成員們也會 照這個樣子去努力,最終團隊也會從中獲益。在一個虛職領導的團隊中,成員們的工作經常會因為領導者看不見而得不到肯定。這時虛職領導就應該明確團隊成員們 工作中的成就和貢獻。關心成員的工作,團隊成員歡迎,團隊成員愿意將來也和他共事,這樣的虛職領導將來就會成為真正的領導。
一對一面談
經常可見在項目收尾時進行成員角色職責定義,并對成員進行評定。這是一個正常的流程。但非正式的一對一面談也會對這個流程起到促進作用。這些非正式面談 的話題應該是團隊成員不愿在小組會議上談的,諸如成員未來的發展機會、對今后領導者或者負責人的確定,以及在得到成員反饋以后,針對當下問題進行平等的溝 通和交流。適時恰當地公布反饋結果能夠區分出這是把問題隱瞞的團隊還是把問題看成一種機會的團隊。團隊表現不佳的責任通常在于團隊結構不佳而不是個人能力 不夠,當然還是要把個人送去培訓來解決一些問題的。如果團隊成員感到他們為了得到獎勵和認可而要相互斗爭,他們就會保留一些可能對團隊有幫助的信息。當團 隊有了問題,高效的團隊領導者首先把關注點集中在團隊結構,而非個人身上。
“不要告訴人們怎么做事,而是要告訴他們做什么,然后讓他們的成就給你驚喜。”——喬治·巴頓
結論
創建一個成功的團隊要考慮相當多的事情。關鍵詞——團結一致、信任、尊重他人的意見、主張以及無畏的行動,這些是偉大團隊的要素,一般說來,這些對任何 成功的團隊都是必需的。讀完本文以后,看一下你的團隊,問你自己:“你工作在一個偉大的測試團隊中嗎?”或者“你愿意盡一切努力來構造一個偉大的測試團隊 嗎?”不要等了,下一秒就可以開始構造“偉大的測試團隊”了。
相逢是開始,相聚是過程,相持是成功。——亨利·福特
最近通過各種渠道發現大家對一些
測試工具的基本情況不太清楚,經常會問類似于watir與watir webdriver的區別,我有1個項目,是用watir還是用
selenium webdriver呢,之類的問題,在這里筆者不才略微總結一下,希望能給大家一個較為清晰的認識。
Waitr與Watir-WebDriver有什么區別?
Watir是非常優秀的一款自動化測試工具。其使用ruby作為腳本語言進行開發,能夠在ie上實現元素定位、操作等自動化任務;
Watir WebDriver是Selenium WebDriver的一個封裝。簡單來說如果selenium webdriver是手機上 的安卓系統,那么watir webdriver就是MIUI。watir webdirver就是將selenium webdriver包裝了一下,使得selenium webdriver的api更加的友好。當然watir webdriver也不是毫無根據的對selenium webdriver進行封裝,watir webdriver使用了watir的api組織形式對selenium webdriver進行封裝,這樣watir的代碼跟watir webdriver的代碼看上去就很”相似”了。這也是大家無法分辨watir與watir webdriver區別的原因。
另外watir webdriver相比較watir有如下的一些優點:
watir webdriver支持多瀏覽器, 而watir只支持ie
watir webdriver支持html5, 而在這方面watir不太明確
watir webdriver對彈出框(js alert confirm)的處理更加友好
watir webdriver支持移動設備,如iPhone和androrid
QTP和watir的區別是什么?
首先最明顯的區別是:QTP是商業工具,其不是免費的;而watir是開源的測試工具,使用免費。
QTP支持腳本的錄制,而watir不可以;
QTP的腳本語言是vbscript,而watir使用ruby進行腳本的開發;
QTP上手很容易,一般測試人員哪怕不會腳本語言都可以通過QPT錄制回放腳本來進行用例的開發;相對來說,watir則需要一定的代碼基礎;
QTP對IE和Firefox都有支持,但是watir只支持IE(盡管firewaitr支持firefox,但是工具目前更新速度不快,可以忽略);
QTP是有軟件界面的,而watir只是一個代碼庫;
那么我應該選擇QTP還是watir?
如果預算允許且自動化測試對瀏覽器的兼容性要求不高的話是可以選擇QTP的;
如果預算有限,但是項目只要求支持IE的話建議選擇watir,學好watir測試人員日后的發展有一定的好處;
如果預算有限,項目又要求支持多瀏覽器,那么推薦使用watir webdriver;
我應該選擇watir webdriver還是selenium webdriver? 從本質上說selenium webdriver 和 watir webdriver是沒有任何區別的,就跟中國男足輸1個和輸10個是沒有任何分別一樣。
從筆者的經驗上來說,watir webdriver的api更加的豐富和友好,如果你可以使用ruby作為開發語言的話,強烈推薦watir webdriver。
如果你的項目要求你使用java javascript之類的語言,那么你只能選擇selenium webdriver了。
我是新手,這么多測試工具我該選擇哪一個作為入門的學習工具呢?
如果你有決心有時間的話,那么推薦watir作為web測試的入門工具,原因是你可以通過watir學習到ruby,js,html,dom等 一系列的知識;然后再轉watir webdriver 甚至是selenium webdriver,學習曲線是相對平滑的;
如果你有決心但沒時間卻又急于求職的話,那么用熟QTP也是一個捷徑。
selenium和webdriver的關系是什么?
webdriver是selenium2的一部分;
webdriver提供了多瀏覽器間統一的api,并將會持續更新,而selenium1將不再維護;
selenium2等于webdriver加selenium1
webdriver比selenium強在哪兒?
wd的api比se更加的面向對象,更加友好;
wd解決了se的軟肋同源問題;
wd多瀏覽器間的行為更加一致;
wd支持iphone和安卓;
se1不再更新,而webdriver社區非常活躍。
作為新人,我想學習腳本語言,我該從哪一門開始呢?
如果你想做web方面的自動化而又想選擇一門腳本語言開始的話,筆者推薦javascript。因為js配合html能做出多種效果,能夠給讓新人很快的獲得成就感。另外前端人員目前相對緊缺,學好js無疑能夠讓你的身價有一定的增加。
如果你只是想學一門腳本語言,那么建議學習python,python的理念是做1件事件從來只有一種方法,盡管沒有選擇,但是至少也不會混亂和迷惑。另外python社區非常活躍,氛圍很好。
如果你想學習watir和waitr webdriver,那么就從ruby開始。ruby入門容易精通難,而且經濟價值沒有py和js那么立竿見影。
總是腳本語言殊途同歸,修行還是要看個人。
概述
在敏捷測試中UI的自動化測試(一般我們也稱這層測試為功能測試或驗收測試,本文單指Web UI的自動化測試)雖然沒有單元測試那么廣為提及,但因為其與最終用戶最近,所以基于用戶場景的UI自動化測試還是有其重要的意義的。使用UI自動化測試對產品的關鍵功能路徑進行驗證及回歸,比起傳統的QA手工執行Test case可以更快地得到反饋,也讓發布變得更有信心。
理想狀況下,我們應該將所有可以固化下來的Test case都自動化起來,而讓我們的測試人員進行更有挑戰性的探索性測試活動。讓機器做已知領域的事兒,讓人對未知領域進行探索。不過理想歸理想,現實是殘 酷的。雖然UI層的測試距離交付最近,但是成本也最高。編寫和維護UI自動化測試需要付出比其他自動化測試更高昂的成本,這也是大多數團隊放棄UI自動化 測試的主要原因。相比較系統的其他部分,UI是一個多變的層,如果UI自動化測試沒有構建好,即使界面的一個微小改動,整個測試集可能就天崩地裂。這也就 是為什么我經常對team里其他人說:對于UI自動化測試,可維護性必須牢記心頭。每當你寫下一行測試代碼時,你就必須記住你又給公司添加了一筆成本,而 且這個成本是持續增長的,如果review code的時候發現哪條測試代碼維護性不好我會毫不猶豫的刪掉。
或許有人覺得這有點小題大作,不就UI測試么,有什么難的。定位元素,然后拿到頁面元素的值與期望進行比較不就可以了。難就難在定位元素上。一般我們會使用Selenium WebDriver, Watir, Sahi等工具驅動瀏覽器,進行元素定位(關于這些工具的詳細使用可以參見官方文檔,后文主要以Selenium WebDriver為示例)。這些工具在定位元素上基本上是大同小異:通過id, name, css, tagName, xpath等方式定位。這些定位方式,從前到后,一個比一個不靠譜。比如這個xpath,好不容易寫出個xpath定位,然后突然有一天前端覺得某個地方 不美觀,插入一個小東西,馬上測試廢掉。看著這種沒有改變功能也把功能測試搞垮掉的現象是不是欲哭無淚。我有時天真的在想如果頁面上每一個元素都有唯一的 id該多好啊。即使沒有唯一的id,有name我也可以接受。不過這一切在遇到ExtJS之后都變了。
遭遇ExtJS
ExtJS是一個非常霸道的前端框架。使用ExtJS后,頁面上幾乎所有的一切都被ExtJS接管。盡管互聯網提 供給用戶的系統鮮有使用ExtJS,但是對于后臺系統使用ExtJS確實帶來了一些便利。使用ExtJS的基本組件就能組裝出一個看起來還不錯,功能強大 的應用。但是ExtJS非常霸道,被他接管后頁面的生成基本上就是個黑盒子,而為了在各個瀏覽器的兼容它在各瀏覽器上生成的html還不一樣。更可恨的是 默認情況下它給元素提供的id都是動態生成的。
在剛選擇這個ExtJS的系統作為我們自動化測試的第一個試點時,我還有點暗暗高興。比 起那些提供給普通用戶使用的豐富多彩的前端來說,這些后臺系統大多中規中矩,使用ExtJS后更是層次分明。而且后臺系統UI的變動也不會太過于頻繁,我 想或許這個系統很容易測試吧。
后來我看到同事代碼里出現:
webDriver.findElement(By.id("ext-gen-1306")) |
我還在想,我們的前端同學真有“創意”,還用這么隨機的名字啊。后來厄運來了,我check out代碼在我這里死活通過不了。Selenium報告找不到指定元素。不是吧,我可是使用id進行定位的啊。通過翻閱ExtJS的文檔發現,原來類似 ext-gen-xxx這類id都是ExtJS動態生成的。好吧,我使用name進行定位吧,后來發現很多元素居然沒有name屬性。再來看看ExtJS 生成的html,基本上把通過xpath進行定位的路給堵死了。要了解ExtJS生成的html,可以去ExtJS官方查看一些Demo。
曙光
閱讀ExtJS文檔我們發現,ExtJS極其強調它的組件模型。而用ExtJS寫的前端代碼也呈現出很好的結構。因為之前曾從事過ASP.NET的開 發,我想是不是可以使用ASP.NET類似的方式先編寫一些小控件類,這些類對ExtJS的基本組件進行包裝。然后利用這些小控件類組裝出一個個頁面。這 樣不僅能把單個元素的定位分散到單個控件類里,而且可以做到極大程度的復用。在傳統的UI自動化測試中我們使用Page Object模式來封裝一個個頁面,但是對于ExtJS來講頁面的粒度還顯得過大。如是模仿ASP.NET的控件模型,我創建了Control, Button, TextBox等一系列基本的控件類。而原來Page Object中的Page不再使用WebDriver直接定位元素了,我們通過這些基本控件組裝頁面。
實現
在這里我用一個簡單的用戶登錄作為例子:
Control是我們的基本類型,所有的控件包括頁面都從這個類派生。
Control只提供了很少幾個方法:
public abstract class Control { protected WebDriver webDriver;
protected Control parent;
public Control(WebDriver webDriver) { this.webDriver = webDriver; }
public String getQuery() { return StringUtils.EMPTY; }
public String getId() { JavascriptExecutor executor = (JavascriptExecutor) webDriver; return (String) executor.executeScript("return " + this.getQuery() + ".id"); } } |
在這里getQuery是一個非常重要的方法,這在后面會介紹。
public abstract class CompositeControl extends Control { protected List <Control> children;
public CompositeControl(WebDriver webDriver) { super(webDriver); children = new ArrayList<Control> (); }
public void addChild(Control control) { this.children.add(control); control.parent = this; } } |
所有的可以包含其他控件的類型都從CompositeControl派生,包括Page。比如下面的Window就是這類元素:
public class Window extends CompositeControl { private String title;
public Window(String title,WebDriver webDriver) { super(webDriver); this.title = title; }
@Override public String getQuery(){ return String.format("Ext.ComponentQuery.query(\"window[title='%s']\")[0]",title); } } |
下面是一個基本控件Button的封裝:
public class Button extends Control { private String text;
public Button(String text, WebDriver webDriver) { super(webDriver); this.text = text; }
@Override public String getQuery() { return this.parent.getQuery() + String.format(".query(\"button[text='%s']\")[0]", text); }
public void click() { webDriver.findElement(By.id(getId())).click(); } } |
ExtJS提供了一個query接口,我們可以利用這個接口傳入一些查詢表達式查詢到頁面上的Ext控件,而這里的getQuery就是每個控件的查詢表達式吧。因為頁面上的ExtJS控件是層次的,所以我們可以利用這種嵌套關系進行精確的定位。
好了,來看看我們的登陸頁面如何封裝吧:
public class LoginPage extends ExtJSPage{ public LoginPage(WebDriver webDriver){ super(webDriver); }
private TextBox txtUserName; private TextBox txtPassword; private Button btnLogin; @Override protected void init(){ txtUserName = new TextBox("userName", webDriver); txtPassword = new TextBox("password", webDriver); btnLogin = new Button("登錄", webDriver);
Window win = new Window("登陸", webDriver); win.addChild(txtUserName); win.addChild(txtPassword); win.addChild(btnLogin);
this.addChild(win); }
public void login(String userName, String password){ txtUserName.setValue(userName); txtPassword.setValue(password); btnLogin.click(); } } |
上面的TextBox和ExtJSPage沒有提供代碼,都很簡單可以自行進行封裝一下(熟悉ASP.NET的同學可能對這里代碼有點眼熟)。
按照這種思路,只要我們封裝好所有的基本ExtJS控件,對于所有的頁面我們剩下的工作就是組裝的工作了。在完成這些之后,我甚至發現使用 ExtJS的應用比那些沒有使用ExtJS的應用更容易進行測試。在這里我們只需要完善我們的基本控件封裝就可以讓我們的測試更佳穩固,而對于編寫測試的 人來說只需要集中精力關注Test case。
不知道得罪了哪路神仙,收到nagios報警,發現有個網站有CC攻擊。看樣子,量還不小,把服務器的負載都弄到40+了,雖然網站還能打開,但打開也是非常的緩慢。如果不是配置高點,估計服務器早就掛掉了。看來又是不一個不眠之夜了。
迅速查看一下nginx的訪問日志:
#tail -f access.log

貌似全是像這樣的狀態。
我先緊急手動封了幾個訪問量比較大的Ip。
- #iptables -A INPUT -s 83.187.133.58 -j DROP
- #iptables -A INPUT -s 80.171.24.172 -j DROP
- ......
|
緊急封了幾個ip后,負載降了一些了,網站訪問速度有所提升了,但是不一會,又來了一批新的Ip, 受不了了,看來要出絕招了。寫了shell腳本,讓他逮著了,就封。發現他攻擊的狀態都相同,每一個攻擊ip后面都有 HTTP/1.1" 499 0 "-" "Opera/9.02 (Windows NT 5.1; U; ru) 的字段,那我們就來搜這個字段。
- #vim fengip.sh
- #! /bin/bash
- for i in `seq 1 32400`
- do
- sleep 1
- x=`tail -500 access.log |grep 'HTTP/1.1" 499 0 "-" "Opera/9.02'|awk '{print $1}'|sort -n|uniq`
- if [ -z "$x" ];then
- echo "kong" >>/dev/null
- else
- for ip in `echo $x`
- do
- real=`grep -l ^$ip$ all`
- if [ $? -eq 1 ];then
- echo iptables -A INPUT -s $ip -p tcp --dport 80 -j DROP
- iptables -A INPUT -s $ip -p tcp --dport 80 -j DROP
- echo $ip >>all
- fi
- done
- fi
- done
|
腳本寫好了。如圖

我們來運行一下,運行幾分鐘后,如下圖所示

經過半個小時的觀察,服務器負載也降到0.幾了,腳本也不斷在封一些CC攻擊的ip。
一直讓他運行著,晚上應該能睡個好覺了。
下來我們來對腳本進行解釋一下。
- #vim fengip.sh
-
- #! /bin/bash
- Touch all #建立all文件,后面有用到
- for i in `seq 1 32400` #循環32400次,預計到早上9點的時間
- do
- sleep 1
-
- x=`tail -500 access.log |grep 'HTTP/1.1" 499 0 "-" "Opera /9.02'|awk '{print $1}'|sort -n|uniq` #查看最后500行的訪問日志,取出包含 'HTTP /1.1" 499 0 "-" "Opera/9.02' 的行的ip并排序,去重復
- if [ -z "$x" ];then
- echo "kong" >>/dev/null #如果$x是空值的話,就不執行操作,說明500行內,沒有帶 'HTTP/1.1" 499 0 "-" "Opera/9.02' 的行
- else
- for ip in `echo $x` #如果有的話,我們就遍歷這些ip
- do
- real=`grep -l ^$ip$ all` #查看all文件里有沒有這個ip,因為每封一次,后面都會把這個ip寫入all文件,如果all文件里面有這個ip的話,說明防火墻已經封過了。
- if [ $? -eq 1 ];then #如果上面執行不成功的話,也就是在all文件里沒找到,就用下面的防火墻語句把ip封掉,并把ip寫入all文件
- echo iptables -A INPUT -s $ip -p tcp --dport 80 -j DROP
- iptables -A INPUT -s $ip -p tcp --dport 80 -j DROP
- echo $ip >>all
- fi
- done
- fi
- done
|
腳本很簡單,大牛略過啊。。。
有什么不明白話,歡迎一起探討學習。
Sql Server系 統內存管理在沒有配置內存最大值,很多時候我們會發現運行Sql Server的系統內存往往居高不下。這是由于他對于內存使用的策略是有多少閑置的內存就占用多少,直到內存使用慮達到系統峰值時(預留內存根據系統默認 預留使用為準,至少4M),才會清除一些緩存釋放少量的內存為新的緩存騰出空間。
這些內存一般都是Sql Server運行時候用作緩存的,例如你運行一個select語句, 執行個存儲過程,調用函數;
1、數據緩存:執行個查詢語句,Sql Server會將相關的數據頁(Sql Server操作的數據都是以頁為單位的)加載到內存中來, 下一次如果再次請求此頁的數據的時候,就無需讀取磁盤了,大大提高了速度。
2、執行命令緩存:在執行存儲過程,自定函數時,Sql Server需要先二進制編譯再運行,編譯后的結果也會緩存起來, 再次調用時就無需再次編譯。
在我們執行完相應的查詢語句,或存儲過程,如果我們不在需要這些緩存,我可以將它清除,DBCC管理命令緩存清除如下:
--清除存儲過程緩存 DBCC FREEPROCCACHE --注:方便記住關鍵字 FREEPROCCACHE 可以拆解成 FREE(割舍,清除) PROC(存儲過程關鍵字簡寫),CACHE(緩存) |
--清除會話緩存 DBCC FREESESSIONCACHE --注: FREE(割舍,清除) SESSION(會話) CACHE(緩存) |
--清除系統緩存 DBCC FREESYSTEMCACHE('All') --注:FREE SYSTE MCACHE |
--清除所有緩存 DBCC DROPCLEANBUFFERS --注: DROP CLEAN BUFFERS |
雖然我們已經清除了緩存,但是sql并未釋放相應占用的內存。 它只是騰出新的空間為之后所執行腳本所用。Sql Server 并沒有提供任何命令允許我們釋放不用到的內存。因此我們只能通過動態調整 Sql Server可用的物理內存設置來強迫它釋放內存。
操作原理是調整內存配置大小。手動操作方法:
1、打開Sql Server Management(企業管理器);
2、打開Sql Server實例的屬性面板;
3、找到內存設置,改變其中的最大服務器內存使用即可 。
使用腳本操作:
--強制釋放內存 CREATE procedure [dbo].ClearMemory as begin --清除所有緩存 DBCC DROPCLEANBUFFERS --打開高級配置 exec sp_configure 'show advanced options', 1 --設置最大內存值,清除現有緩存空間 exec sp_configure 'max server memory', 256 EXEC ('RECONFIGURE') --設置等待時間 WAITFOR DELAY '00:00:01' --重新設置最大內存值 EXEC sp_configure 'max server memory', 4096 EXEC ('RECONFIGURE') --關閉高級配置 exec sp_configure 'show advanced options',0 GO |
下面提供內存查看功能的一些腳本語句:
--內存使用情況 SELECT * FROM sys.dm_os_performance_counters WHERE counter_name IN ('Target Server Memory (KB)','Total Server Memory (KB)') |
--查看最小最大內存
SELECT configuration_id as id,name as 名稱,minimum as 配置最小值, maximum as 最大值,
is_dynamic as 是否動態值, is_advanced as 是否優先, value_in_use AS 運行值,
description as 描述 FROM sys.configurations
最近在做應用的性能優化,在review代碼的過程中積累了一些規則和經驗。做到這些規則的目的很簡單,就是寫出“優美”的代碼來。
1、注釋盡可能全面
對于方法的注釋應該包含詳細的入參和結果說明,有異常拋出的情況也要詳細敘述;類的注釋應該包含類的功能說明、作者和修改者。
2、多次使用的相同變量最好歸納成常量
多處使用的相同值的變量應該盡量歸納為一個常量,方便日后的維護。
3、盡量少的在循環中執行方法調用
盡量在循環中少做一些可避免的方法調用,這樣可以節省方法棧的創建。例如:
- for(int i=0;i<list.size();i++){
- System.out.println(i);
- }
|
可以修改為:
- for(int i=0,size=list.size();i<size;i++){
- System.out.println(i);
- }
|
4、常量的定義可以放到接口中
在Java中,接口里只允許存在常量,因此把常量放到接口中聲明就可以省去public static final這幾個關鍵詞。
5、ArrayList和LinkedList的選擇
這個問題比較常見。通常程序員最好能夠對list的使用場景做出評估,然后根據特性作出選擇。ArrayList底層是使用數組實現的,因此隨 機讀取數據會比LinkedList快很多,而LinkedList是使用鏈表實現的,新增和刪除數據的速度比ArrayList快不少。
6、String,StringBuffer和StringBuilder
這個問題也比較常見。在進行字符串拼接處理的時候,String通常會產生多個對象,而且將多個值緩存到常量池中。例如:
- String a="a";
- String b="b";
- a=a+b;
|
這種情況下jvm會產生"a","b","ab"三個對象。而且字符串拼接的性能也很低。因此通常需要做字符串處理的時候盡量采用StringBuffer和StringBuilder來。
7、包裝類和基本類型的選擇
在代碼中,如果可以使用基本數據類型來做局部變量類型的話盡量使用基本數據類型,因為基本類型的變量是存放在棧中的,包裝類的變量是在堆中,棧的操作速度比堆快很多。
8、盡早的將不再使用的變量引用賦給null
這樣做可以幫助jvm更快的進行內存回收。當然很多人其實對這種做法并不感冒。
9、在finally塊中對資源進行釋放
典型的場景是使用io流的時候,不論是否出現異常最后都應該在finally中對流進行關閉。
10、在HashMap中使用一個Object作為key時要注意如何區分Object是否相同
在jdk的HashMap實現中,判斷兩個Object類型的key是否相同的標準是hashcode是否相同和equals方法的返回值。如 果業務上需要對兩個數據相同的內存對象當作不同的key存儲到hashmap中就要對hashcode和equals方法進行覆蓋。
nmon_analyser統計分析工具可以很好的分析 指定時間段的系統性能。
nmon工具分為2部分:AIX性能監控和數據采集的工具和對nmon采集數據進行分析工具。
AIX性能監控和數據采集的工具:
newer_nmon4aix12e.tar.gz適用操作系統為:
aix51
aix522
aix527
aix530
aix534
aix535
aix536
aix537
aix61
nmon12f_aix612.gz 適用操作系統為:
aix6.1.2
nmon采集數據進行分析工具:
nmon_analyser.zip
把信息收集工具ftp到aix操作系統,并解壓
gunzip newer_nmon4aix12e.tar.gz
tar -xvf newer_nmon4aix12e.tar
運行AIX性能監控和數據采集的工具:
通過信息收集工具在Aix平臺的運行,收集指定一段時間的系統信息,生成hostname_yymmdd_hhmi.nmon文件:
例:
./nmon12e_aix537 -fAW -m /nmon/log -s 60 -c 2400
在/nmon/log 目錄下生成格式為hostname_yymmdd_hhmi.nmon的文件,命令間隔為60秒鐘,重復運行1440次(即1天)。
把該文件下載到本地,解壓nmon_analyser.zip 文件,
打開nmon analyser v33e3.xls
點擊按鈕Analyse nmon data
選擇hostname_yymmdd_hhmi.nmon文件進行分析就能得到所要的數據。
nmon分析工具下載:
http://wenku.baidu.com/view/aa1f3312cc7931b765ce156c.htm
http://hi.baidu.com/jiaju111/blog/item/ea2b2464f73213f8f6365428.htmll
很多時候我們需要利用參數在存儲過程中重新組織SQL語句,在存儲過程中拼接的SQL語句只是一個字符串,不會被直接執行,所以加一個execute執行它就可以了。具體看如下演示代碼:
代碼:
set ANSI_NULLS ON set QUOTED_IDENTIFIER ON go -- ============================================= -- Author: yy -- Create date: 2012-1-17 -- Description: 存儲過程SQL字符串拼接示例 -- ============================================= CREATE PROCEDURE [dbo].[Test] @FileName varchar(10), --字段名 @Operator varchar(1), --操作符 @FileValue varchar(10) --字段值 AS DECLARE @TempSql varchar(100) --臨時存放sql語句 BEGIN set @TempSql= 'select * from Comment where ' + @FileName + @Operator + char(39) + @FileValue + char(39) --拼接sql字符串,char(39)為單引號 execute(@TempSql) --執行sql字符串 END |
測試:
execute Test 'newsid','>',4 |
在這解釋一下“ALTERPROCEDURE [dbo].[Test]”至“AS”之間的代碼和“AS”至“BEGIN”之間的代碼有什么區別,像我這種SQL新手應該會有疑問:為什么@TempSql要定義在“AS”至“BEGIN”之間?因為,“AS”至“BEGIN”之間定義的為臨時變量,前邊必須加DECLARE,和其他語言中普通變量的使用方法相同;而“ALTER PROCEDURE [dbo].[Test]”至“AS”之間定義的是存儲過程被調用時傳入的必要參數,必須在調用的時候就賦值,不可以加DECLARE,可以理解為字符常量,一旦調用時被賦值,就再無法改變,就上邊例子來說,類似@FileName=’xxx’的寫法是錯誤的。因為@TempSql只是用來接受SQL語句的臨時變量,沒有初值,但必須接受值,所以要定義在“AS”至“BEGIN”之間。
“通過一次真正徹底地代碼審查(code reviews),仔細閱讀你的代碼,找出問題,這是我知道的最好的方式去檢測早期的bug,但是他們很少去這樣干過。某種意義上是因為他們花了大量的時間去寫好代碼,但是我認為主要是因為絕大部分程序員害怕其他人審查自己的代碼。作為專業的程序員我們要克服阻力,如果你不愿意別人閱讀你的代碼,然后只是按照自己的意愿寫,如果其他人沒法讀懂它,又怎能讓別人使用呢?”Jim Waldo – 《Java語言精粹》的作者
我強烈贊同code review 是軟件生命周期管理中重要的一部分,因為它能幫助我們交付高質量代碼、合格作品。
傳統上code review僅是一個形式,通常在代碼提交之前由團隊負責人或高級程序員負責。在敏捷開發環境中,通過團隊合作code review 更系統化,代碼的目標和期望應該能用編碼指南清晰的定義出來,code review的目標是協同合作,而不是查錯。總之code review對整個團隊尤其每個程序員都有好處,所以每個人都應該參與進來。

code review的好處:
俗話說三個臭皮匠賽過諸葛亮,code review 更易于發現代碼bug等問題
3、保證代碼質量以及提高代碼可讀性
2、團隊之間建立信任
1、指導初級程序員
編碼標準是獨立于語言的,對于Java 程序員來說,我想從以下幾個范圍來做code review
Java code review的標準:
合適的變量聲明;如:實例變量還是靜態變量、常量等
9、性能問題;如:當沒有線程安全問題時使用ArrayList,HashMap替代Vector,Hashtable
8、內存問題;如:本應使用對象重用或者對象池時卻被不恰當的初始化,沒有在finally塊中關閉昂貴的資源。
7、數據訪問問題:從數據庫一次獲取數據太多,請求太多的數據庫調用。
6、線程安全問題;如:Java API類像SimpleDateFormat,Calendar,DecimalFormat等不是線程完全的,在JSP中聲明變量也不是安全的,存儲狀態信息在Struts action類中或者多線程servlet也不是線程安全的。
5、對錯誤的處理:異常拋出而沒有保持原始模型(希望Java7修復它),沒有記錄到日志系統中
4、System.out常被log4j替換
3、設計問題:沒有重用代碼,沒有清晰的責任分離。如:業務邏輯嵌套在servlet中,而沒有使用業務邏輯層,可視化元素(如HTML,CSS)嵌入在后臺。
2、代碼文檔:沒有注釋,沒有頭文件等
1、從給定的框架中遵循最佳實踐:如Spring3中注解替代xml文件對于IOC, 對于每一個簡單的部署使用外部屬性替代硬編碼值等
你應該為團隊做個code review的文檔和模板,隨著項目的開始同步更新,學習更多你項目中選擇的軟件。
工欲善其事必先利其器
code review 工具:
3、Crucible 是 Atlassian公司的工具用來不間斷處理的審查工作,Crucible能做代碼審查而且高度集成在JIRA和FishEye中,支持Subversion、Git等其他類型的VCS。一個通用的例子就是Crucible提供一個轉換憑證的工作流,從打開》審查》解決,另一種情景是在代碼改變后check- in了之后自動審查。
2、Gerrit ,Gerrit一個基于web的code review系統。通過Git版本控制系統能方便在線做code reviews。
1、Checkstyle: 并不只是一個code review 工具,更是一個開發工具確保開發者的代碼遵循標準,在每一次code review中節省時間。
最重要的是,使用Checkstyle能使代碼檢查成為一個相對簡單的任務,你可以把code review 作為日常活動中的一部分而不需要在項目結束的時候才開始,因為那時候項目的交付期限讓你的生活一團糟了。