qileilove

          blog已經轉移至github,大家請訪問 http://qaseven.github.io/

          如何評估測試人員績效

           每一段時間, 就會有人開始討論QA的performance要如何評量, 有些人會提出以下的index
            - 計算所找到的Bug個數
            - 在一段時間內所開立的測試個案
            - 所執行的測試個案個數
            - 自動化測試個案個數/ 所有測試個案個數
            - 測試涵蓋度
            這些index的缺點, 是缺乏考慮整個環境或是項目的狀況, 容易會忽略一些會影響的變量. 作者認為如果沒有根據context就來衡量個人的績效, 是一件愚蠢的事情.
            例如有些狡猾的測試人員, 可能會采取一些策略來達到你的index的標準, 但是卻危害了整個團隊的質量.舉各例子來說: 如果manager說要評量engineer每周所找到的bug數, 并且訂定每周的標準是10個bugs. 這時候會發生什么事, 每周engineers會想辦法找到10個bugs, 但是對于多找的bugs, 有些engineers可能會考慮放到下周再提報出來, 這樣才能確保下周他比較容易達到pass的criteria. 這代表bug report是無法反映實時的狀況, 很能是慢一周. 所以你有可能會誤解這時候狀況不嚴重, 導致你會因為錯誤的數據而做出不當的決策.
            為什么會這樣呢? 主要是因為有些短視的人, 想要用簡單的方法, 去解決困難的問題. 可是這個人績效問題, 真的是沒有簡單的公式就可以衡量出來的. 而且有些衡量是很主觀的, 并且也外受到一些外在因素的影響, 像是所處的工作環境, 或是使用的工具, 或是你本身的個性, 或是老板是否善于鼓勵員工...等等, 這些因素都會讓相同的人, 產生不同的結果.
            另一個我常見的問題, 那是訂定不切實際的目標. 像是"找出主要的bugs", 試問你如何界定他是主要的bug? 并且主要的bug是否代表就是重要的bug呢?
            Over-promise和under-deliver也是個嚴重的問題, 沒有根據自己的能力來訂出適當的目標. 另一個相關的就是, manager給所有人都是相同的pass criteria, 既然每個人的能力不同, 你就必須要給每個人設定不同的標準.
            作者建議測試人員試著要和你的經理, 去學習如何訂定SMART的衡量標準. 因為每個人能力不同, 項目環境不同, 沒有一體適用的標準. 此外也要記得align managers, product teams或是company的goal. (當然啊, 最后這點是比較爭議的, 因為你的career path不一定和公司一樣)

          posted @ 2014-09-18 10:03 順其自然EVO 閱讀(202) | 評論 (0)編輯 收藏

          有效性的QA審核檢查單

          經常有這么一個問題:QA為什么對過程改進有幫助。答案當然與如何理解、如何管理、如何處理QA與項目之間的關系密不可分。這里有很多議素我們需要好好探討。其中一個比較實在一點的,就是如何讓QA可以在審核之中,發現項目是否遵從標準規程之余,還能否發現可以提高項目效率的機會,并且提出有針對性地建議,讓項目可以更有效地達成項目的目標。
            QA審核的內容,通常都可以從審核的檢查單之中看到一個端倪。如果檢查單里的問題,他們的答案明顯與項目的效能無關的,按照這樣的檢查單審核,就當然不能發現如何幫助項目提高效能。所以我提到QA需要制定“有效性”的檢查單。
            問題立刻出現了,就是“什么樣的檢查單才是有效性的檢查單?”本文的目的就是希望解答這個問題。
            讓我首先作幾個聲明:
            1)當我們談“有效性”審核的時候,我們不是說“遵從性”的審核不好。我希望強調一點:“遵從性”是制度化與成熟程度的基礎。沒有了遵從性的團隊、組織,是不成熟的,過程操作一般是低效的。只有具備了一定程度上的遵從性,過程管理才能開始生效。請大家明白這一點。
            所以我們談有效性審核,其實是說在遵從性審核的基礎上,考慮有效性的問題。這是一個能力水平的提升,而不是一個對標準規程的叛逆。
            2)要提升過程的效能,因為每一個項目團隊能力、風格不一樣,是需要針對性的。這些針對性,就反映在調整檢查單上面。我也遇到過這樣的問題:“QA的標準規程里有標準的檢查單,我們是否必須者用同樣的檢查單,才是符合標準的QA審核?”
            我希望說明的就是:在符合標準要求的情況下,進行部分的調整是可以容許的。這個概念,就體現在 CMMI 要求標準規程里提供“適配”,并要求制定適配的準則。我們需要了解這一點。當然這個需要對規程與過程管理具備一定的了解與判斷能力。這些知識與經驗是需要積累的。
            3)千萬不要認為只有一個方法制定檢查單。做任何事情,都有很多實際方法、途徑可以完成。這里只是一個案例。我希望大家可以看到要考慮什么問題,要如何思維,然后發展自己的觀察、分析、判斷等能力,這才是最重要的。
            好了,我們就談談如何制定“有效性”的檢查單吧。我拿了一個案例來說明。這個案例是一個組織在使用的,用來審核“估算”的標準檢查單。這里就來說明如何從這個檢查單開始,加入“有效性”因素的考慮,來達到一個既滿足本來的遵從性審核任務,也可以反映項目的操作是否有效的檢查單。
            制定有效性檢查單的步驟:
            1)我們要知道,“有效性”就是能夠達到目標。如果沒有目標,或是QA不清楚目標,有效性是無從談起的。
            所以QA一定需要知道檢查單上面每一個條款,如何與項目的目標相關聯。
            2)符合性與有效性既相互關連,也相互獨立。如果要過程有效,我們需要建立制度化。制度化的一個條件,就是每一個員工都需要有愿意遵從規程,符合規程的意愿。我們需要用一種大家都了解的,一致的方法,來處理日常的、或是不理想的、特殊的情況。要過程改進,我們就需要有遵從的紀律。
            所以有效性是建立在“遵從、符合”的基礎上的。
            另一方面,如果規程制定得不合理,或是缺乏靈活性,來適應不同的項目、技術、產品。比如,無論項目大小,周期長短,都要求用“Wideband Delphi”進行估算。這就不靈活了。短周期的小項目就很不適合了。又或是要求嚴格復雜的測試用例分析、策劃等等,也有同樣的問題。那么,項目的過程就是符合,也不會有效。
            所以QA需要具備專業態度與獨立精神來處理現實中“符合性”與“有效性”的沖突與一致。
            3)因為過程有效性,就是能夠達到目標。那么,審核過程的有效性,就需要判斷。所以一定要求審核的人(QA)具備這種判斷能力,而不是期待把檢查單細化到沒有判斷能力的人也可以實施。
            舉一個例子,如果檢查單里的問題是:
            一)估算是否按計劃的時間完成? 那么誰都可以判斷。但這不是有效性的。因為我們沒有考慮不按計劃的時間完成是否影響項目的目標。
            二)估算完成的時間是否對項目達成目標造成風險?這個問題才是有效性的。那么,就需要判斷了。我們不能夠有效地定義:超時兩天就可以,三天就不可以。這在大部分的情況底下其實是不可能的。能夠回答這類問題的人,需要有判斷能力。
            這是為什么我們要求QA不斷提高自己的能力的原因。
            談到QA能力,我在這里先問一下:我們可否識別“子過程的目標”與“審核子過程的目標”?我們需要能夠分辨這個。舉一個例子:“同級評審”的目標,就是發現缺陷。但是為什么我們要審核“同級評審”這個子過程?那么目標就可以有很多:比如,作為考核的依據;查看項目是否遵從標準規程;評價這個子過程的操作實效;發現項目是否具備實施同級評審的技巧,等等。我們一定要能夠分辨這些不同層次的目標。
            4)總結一下:要從事有效性的審核也好,制定有效的規程也好,就需要考慮:
            每一個條款或是步驟是為什么,要解決什么問題? (要解決什么?)
            這個條款或是步驟有什么后果? (有什么后果?)
            這些要解決的問題與實施后果如何影響項目的目標?(對項目的影響與風險)
            這些就是QA需要能夠回答的問題。
            QA需要養成一個考慮這些問題的習慣來建立自己回答這些問題的能力。
          5)假設我們已經具備這些能力,那么制定有效性的檢查單就不難了。第一個考慮的概念,就是“層次”。假設EPG提交了一個檢查單,如下:
            檢查項包括:
            是否按時進行了二次估算?
            作者是否參與集成測試活動估算?
            作者是否參與各模塊開發活動估算?
            開發相關人員是否都參與集成測試活動估算?
            是否會議方式估算?
            詳細設計估算是否完整有效?
            編碼估算是否完整有效?
            單元測試估算是否完整有效?
            集成測試用例估算是否完整有效?
            集成測試執行估算是否完整有效?
            偏差超過約定的偏差值是否再次估算?
            是否完整填寫估算人員姓名及過程數據?
            估算報告是否及時歸檔?
            我們可以看到,這個單里的條款,它們的重要性不是同一個層次的。這不是一個制定任何規程的好習慣。我們需要知道把同一個層次的條項羅列在同一個層面上。比如:
            是否按時進行了二次估算?
            作者是否參與集成測試活動估算?
            有哪些與這個估算相關的活動?
            作者是否參與各模塊開發活動估算?
            開發相關人員是否都參與集成測試活動估算?
            是否會議方式估算?
            有哪些與這個估算相關的績效?
            詳細設計估算是否完整有效?
            編碼估算是否完整有效?
            單元測試估算是否完整有效?
            集成測試用例估算是否完整有效?
            集成測試執行估算是否完整有效?
            偏差超過約定的偏差值是否再次估算?
            估算報告是否及時歸檔?
            層次這個概念在很多地方都有應用??纯次覀児镜慕Y構,你就可以體會到層次的普遍應用程度。不同層次的,就不會在同一個級別的領導之下。比如,生產、財務、市場都是在同一個層次上。我們不會把“融資”放在同一個層次,它一定是在財務底下的。
            層次會影響效率。比如我們的檢查單,合理的層次,會讓員工與QA更明確各個部分的重要性,在開展工作的時候也可以更好地掌握注意力。
            要了解層次,可以這樣看:同一個層次的,是比較相對獨立,互不影響的。低一個層次的,是它上面層次的一部分,或者說,是會對上面層次的提供貢獻。比如:“是否按時進行了二次估算?”和“有哪些與這個估算相關的活動?”是相對獨立的。但“作者是否參與各模塊開發活動估算?”就是與估算相關的活動之一。這個有點像CMMI的級別定義原則。這些都是幫助有效的理念,并且被廣泛應用在過程管理與以外的很多方面的。
            檢查單的組織可以考慮條項的層次。
            如果不了解這個“層次”的觀念,就請你提問。我們務必交流清楚。
          6)下一步就是分辨符合性與有效性。還記得上面提到的三個考慮么:
            每一個條款或是步驟是為什么,要解決什么問題?(要解決什么?)
            這個條款或是步驟有什么后果? (有什么后果?)
            這些要解決的問題與實施后果如何影響項目的目標?(對項目的影響與風險)
            我們要知道,如果問題是:
            是否按時進行了二次估算?
            我們的答案就會是“是”或是“否”。這個問題要解決的,就是:你是否遵從計劃?是一個符合性的問題。所以解答這個問題一點都不難。但是如果問題是:
            完成二次估算的時間對項目有什么影響?
            這個問題要知道完成時間對項目有什么影響,要知道估算知否有效。這才是有效性的檢查項?;卮疬@個問題就一點都不簡單。我們要知道這個完成時間有什么后果。早于計劃的要求完成?按計劃時間要求完成?超時了?超了一點點?超了很多?還有更厲害的:這個模塊/任務是否在關鍵路徑上面?我們要知道所有這些,可能還有其他的,才能作一個判斷。做這個判斷,就要知道關鍵因素與它們的后果。比如:
            估算晚了兩個星期。但這是一個2 年的項目,而且這個任務可以有5 個星期的靈活性。那么,這樣晚了兩個星期的作用就不很大。他是不符合。但后果不大。
            如果估算晚了兩個星期,項目是2 年的項目,而且任務是在關鍵路徑上。后果就可能讓項目延誤兩星期。是大概2 %的延誤。這樣的延誤,絕大部分客戶可能發現(重要),但都不會太計較的(不嚴重)。
            如果估算晚了兩天,任務在關鍵路徑,項目是2個月的項目。這樣問題就嚴重好多了。
            所以如果要作有效性審核,我們需要知道關鍵因素,以及他們的后果。
            一個步驟的目的,如果單單是看項目是否符合,而不考慮后果的,這樣的管理,目的就是在于“限制行為”。限制或是規范行為的壞處,就是引起項目的抗拒。所以“限制行為”的管理很難有好效果的。
            7)那么,我們可以開始把這些關鍵因素組織一下,為把檢查單改變成為有效性的檢查單做準備。我們需要把原本的改成符合與有效兼顧,并且增加一些遺漏的關鍵因素。首先,我們可以把下面的條項:
            是否按時進行了二次估算?
            作者是否參與集成測試活動估算?
            有哪些與這個估算相關的活動?
            作者是否參與各模塊開發活動估算?
            開發相關人員是否都參與集成測試活動估算?
            是否會議方式估算?
            有哪些與這個估算相關的績效?
            詳細設計估算是否完整有效?
            編碼估算是否完整有效?
            單元測試估算是否完整有效?
            集成測試用例估算是否完整有效?
            集成測試執行估算是否完整有效?
            偏差超過約定的偏差值是否再次估算?
            估算報告是否及時歸檔?
            小心看一下,用提高效率的觀點,考慮每一個條項的后果,改編成有效性的條款,如下:
            估算完成的時間對項目的影響:
            作者樂于承諾估算結果的程度與原因:
           ?。〞簳r忽略)
            估算的方法與過程保證了估算的合理性:
            (暫時忽略)
            (暫時忽略)
            估算結果得到維護與使用:(包含了“估算報告是否及時歸檔?”)
            我們現在加入我提到的三個因素:
            負責任務的人親自牽頭估算
            大家(項目組與客戶)對任務范圍與內容的認識是一致的。
            方法與參考數據的使用是合理的
            檢查單就變成:(斜字代表這個步驟新加進來的。)
            估算完成時間對項目的影響:
            作者樂于承諾估算結果的程度與原因:
            負責任務的人牽頭估算
            估算的方法與過程保證了估算的合理性:
            大家對任務范圍與內容有一致的認識
            估算方法適合項目要求
            方法的調整是合理的
            估算結果得到維護與使用:
            項目計劃使用了估算結果來安排任務
            如果需要,可以把原來的符合性的放回去??梢园阉鼈兎旁诹硪粋€部分,也可以把它們放在適合的層次。比如:
            估算完成得時間對項目的影響:
            ·  是否按時進行了二次估算?
            作者樂于承諾估算結果的程度與原因:
            ·   負責任務的人牽頭估算
            估算的方法與過程保證了估算的合理性:
            ·  大家對任務范圍與內容有一致的認識
            ·  估算方法適合項目要求
            ·  方法的調整是合理的
            ·  是否會議方式估算?
            ·   作者是否參與各模塊開發活動估算?
            ·   開發相關人員是否都參與集成測試活動估算
            ·  (如果用Delphi方法)偏差超過約定的偏差值是否再次估算?
            估算結果得到維護與使用:
            ·   估算結果在計劃里項目計劃使用了估算結果來安排任務
            ·   估算報告是否及時歸檔?
            以往估算的績效:
            ·   詳細設計估算是否完整有效?
            ·   編碼估算是否完整有效?
            ·   單元測試估算是否完整有效?
            ·   集成測試用例估算是否完整有效?
            ·   集成測試執行估算是否完整有效?
            現在的檢查單包含了本來的條項,也包括了從有效性的角度的內容。但有效性方面還是不充分的。所以我們需要每一個條項都問這個問題:
            我如何可以判斷這些條項的有效程度?這個也需要我們了解過程的關鍵因素!
            上面已經提到過估算的完成時間如何影響項目。包括延誤的比率、客戶的要求、任務是否在關鍵路徑等等。其他的,我就把我考慮的加到相關的條項底下,如下:
            估算完成得時間對項目的影響:
            ·   是否按時進行了二次估算?
            ·   延誤程度對比項目的里程碑與周期的影響有多嚴重
            ·   延誤如何對比任務的關鍵路徑寬容時間
            ·   考慮這個任務的關鍵性與項目對它的依賴程度
            作者樂于承諾估算結果的程度與原因:
            ·  負責任務的人牽頭估算
            ·  判斷負責人對結果的信心
            ·   查看負責人以前的承諾(按時完成任務)表現
            估算的方法與過程保證了估算的合理性:
            ·  大家對任務范圍與內容有一致的認識
            o    在如下面兩條羅列的估算活動以及其他情況底下,多方面的干系人的表達是一致的。
            o    如果有開估算會議,討論覆蓋足夠的內容與議素,但進行順利,沒有理解不一致的跡象。
            o    (還可以有其他的蛛絲馬跡來判斷)
            · 估算方法的效能適合項目的目標
            ·  參考與使用的歷史數據與對比、調整的合理性
            ·  是否會議方式估算?
            ·  作者是否參與各模塊開發活動估算?
            ·  開發相關人員是否都參與集成測試活動估算?
            ·   偏差超過約定的偏差值是否再次估算?
            估算結果得到維護與使用:
            ·  項目計劃使用了估算結果來安排任務
            ·  項目明確有哪些活動受這個估算結果影響(可以更好處理將來的延誤)
            ·  估算報告是否及時歸檔?
            以往估算的績效:
            ·  詳細設計估算是否完整有效?
            ·  編碼估算是否完整有效?
            ·  單元測試估算是否完整有效?
            ·  集成測試用例估算是否完整有效?
            ·  集成測試執行估算是否完整有效?
            希望大家可以關注到這里檢查點之間的關聯性,以及我們的步驟如何影響項目的專注。
           8)上面的表就是一個從你們本來使用的檢查單,增加了審核有效性的角度而得來的。讓我們把這個資料放到一個表格里:
            大家可以補充“檢查方法”這一列。大家也可以按這個思路,增、減因素與檢查項的內容與細節。其實標準的檢查單,也應該可以讓大家按情況“調整”。請留意,我不說修改,而說調整,含義是:我們需要按標準的思路,讓它更有效,而不是把內容隨意變動。我說的明白么?
            這樣的表格跟以前的有兩個分別:
            語氣是開放式的,不鼓勵是否地打勾。每填一個條項,都希望QA知道需要明白后果。以后習慣了這樣思維,我們就可以不那么關注語氣的問題了。我鼓勵大家目前還是在意一點比較好。
            內容多了一些有效性的關鍵因素。這個是可以積累的。我們需要從工作中觀察,以需要多討論,多交流,多看書。一位QA發現一個關鍵因素之后,不能單單加到自己的檢查單里。QA小組最好組織討論會議,讓每一位QA都知道一個關鍵因素的來龍去脈,原因后果。這樣才能讓QA團隊的經驗建立起來。
            也請留意,每一個關鍵因素都有數個檢查項,但對項目來說,同一個關鍵因素的所有檢查項加起來只有一個后果,我們不單單在乎是否打勾,而是要判斷項目的表現有什么后果。我們可以按關鍵因素預設一些后果。比如:超越預期、沒有風險、有可承擔的風險、有不可承擔的風險、不可承擔又不可緩解的風險等。項目的操作狀態,就是這幾個關鍵因素的執行后果。每一個關鍵因素的后果,都取決于它下面的檢查項。但我以前也說過,我覺得每一項打分,然后加起來,不是一個很好的方法,因為有些條項的重要性,會因為其他的條項內容而改變的。我們可以這樣做,然后參考那些分數。只是不要把分數看成決定一切就可以了。
            想象一下,如果我們的QA審核報告都是這樣的,大家就會越來越清楚如何提高項目的效率。將來的過程問題,大部分都可以從QA報告的歷史資料找到答案。過程改進將會變得非常容易。
            9)不要以為這是唯一的有效性檢查單,所以不要把這個看成經典。有效性的檢查單可以有很多。比如,我們如果不一定要求從你們本來的檢查單開始,我們可以從我提到的三個因素開始。那么,檢查單就會不一樣。
            我們要能夠分辨得細一點,要爭取了解細一點的議素。比如,不要把上面的檢查單看成固定不變的。但也不要以為什么事情都不能是固定的。有些事情固定了,就不靈活,不能適應不同的情況,效果就不好。有些事情是不會改變的,改了,就沒有效果了。如何判斷呢?
            基本上:
            因果關系是不會變的。沒有了因,就不會有果。
            解決方案,就幾乎永遠都會有不同的方案,達到相同或是接近的效果。
            要進步,我們就要慢慢掌握這些理念,并且能夠把它應用到自己的工作上。

          posted @ 2014-09-18 09:56 順其自然EVO 閱讀(481) | 評論 (0)編輯 收藏

          使用WebInject測試WebService

          本文通過學習WebInject官網教材,然后測試自己開發的WebService。
            首先,我們有一個無敵的HelloWorld服務,這個服務超級簡單,相信大家都很熟悉。就不介紹了。
            然后,創建SOAP消息,根據我自己的經驗,soap消息就是把webservice的WSDL文件中的輸入輸出message給soap化了。具體看一個例子:
            WSDLmessage格式:
            <wsdl:messagename="sayHelloRequest">
            <wsdl:partname="parameters"element="ns:sayHello"/>
            </wsdl:message>
            sayHello元素的格式:
            <xs:elementname="sayHello">
            <xs:complexType>
            <xs:sequence>
            <xs:elementminOccurs="0"name="args0"nillable="true"type="xs:string"/>
            </xs:sequence>
            </xs:complexType>
            </xs:element>
            轉成soap消息格式就是:
          <?xmlversion='1.0'encoding='UTF-8'?>
          <soapenv:Envelopexmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"xmlns:q0="http://ws.apache.org/axis2"xmlns:xsd="http://www.w3.org/2001/XMLSchema"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
          <soapenv:Body>
          <q0:sayHello>
          <args0>pengyusong</args0>
          </q0:sayHello>
          </soapenv:Body>
          </soapenv:Envelope>
            可以看到soap消息就是把message中的part解析為soap中的body中內容。
            再然后就是測試用例文件:
          <testcasesrepeat="1">
          <case
          id="1"
          description1="WebServicesSample-HelloWorld"
          url="http://localhost:8080/axis2/services/HelloWorld?wsdl"
          method="post"
          posttype="text/xml"
          postbody="file=>doGoogleSearch.xml"
          verifypositive="\<return>Hello,mynameispengyusong\</return>"
          />
          </testcases>
          最后在執行這個測試用例:通過GUI方式的話,在config.xml文件中指定我們的測試用例文件即可。然后在GUI界面點擊Run就會執行成功。
          -------------------------------------------------------
          Test:test_google.xml-1
          WebServicesSample-GoogleSearchAPI
          Verify:"Hello,mynameispengyusong"
          PassedXMLParser(contentiswell-formed)
          PassedPositiveVerification
          PassedHTTPResponseCodeVerification(notinerrorrange)
          TESTCASEPASSED
          ResponseTime=0.015sec
          -------------------------------------------------------
          StartTime:WedOct3016:46:342013
          TotalRunTime:0.409seconds
          TestCasesRun:1
          TestCasesPassed:1
          TestCasesFailed:0
          VerificationsPassed:3
          VerificationsFailed:0
          AverageResponseTime:0.015seconds
          MaxResponseTime:0.015seconds
          MinResponseTime:0.015seconds

          posted @ 2014-09-18 09:31 順其自然EVO 閱讀(229) | 評論 (0)編輯 收藏

          LoadRunner之調用遠程負載

          原因:
            據經驗,每生成一個虛擬用戶,需要花費負載生成器大約 2M-3M 的內存空間。通常運行 controller的主機很少用作負載生成器。負載生成器的工作多由其他裝有 LR Agent的PC 機來擔任。如果負載生成器內存的使用率大于了 70%,負載生成器就會變成系統的瓶頸,導致性能測試成績下降。這種問題需要添加負載生成器來解決。一臺 512M內存的 PC 機大約可以生成 80 個左右的負載,而一臺 256M 內存的 PC 機大約可以生成50到 60 個左右的負載。
            實現借用遠程加壓機:
            所以通常做大量用戶的負載時,就需要借用其它的機器來加壓。此時借用的加壓機需要首先安裝LoadRunner的Load Generator這個部分組件。再按照以下操作執行:
            LoadRunner在測試web應用的時候,最常用的是分布式性能測試,也就是說由多個負載發起機向應用服務器發起請求。
            那么LR(loadrunner)是如何做到的呢?
            首先,這要多虧于LR的架構,LR是由controller做測試控制的,scenario做測試場景的控制,Vuser模擬用戶和load generator做負載產生。
            這樣我們就很容易想到,只要分布的其他負載發起機上有Vuser和load generator就能做分布式測試了。
            對了,LR就是這么做的,它通過MI listener(跨防火墻監聽)來達到以上的目的,默認接受數據的端口是54345,默認發送數據的端口是50500。
            第一步,我們要安裝LR,這樣的教程網上已經很多了我就不詳述了。不過要注意一點,LR在win2000上安裝后就自動打開了上述的端口,而在winXP上需要手動開啟。具體步驟見第二步。
            在負載發起機上我們要安裝如下組件
            第二步,我們要啟動監聽的服務,如下步驟
            啟動代理
            
            設置代理
            按Settings,在這個選項卡中我們可以配置一些用戶名和密碼(如果有需要的話)
            
          第三步,設置場景來連接負載發起機
            創建場景
            
            點擊load generator圖標
            
            點擊Add按鈕,來增加負載發起機
            
            在Name 處填入IP地址,點擊確定
            
            這樣我們就配置了一個負載發起機,重復上述步驟我們可以添加多個負載發起機。
            然后,我們測試下能不能連上負載發起機,選中負載發起機,點擊Connect
            
            看到Status里出現Ready字樣,我們就連上了一個負載發起機。
            按確定退出,會在任務欄上看到如下圖標
            

          posted @ 2014-09-18 09:30 順其自然EVO 閱讀(4756) | 評論 (0)編輯 收藏

          Selenium下載百度音樂并驗證

          package baidu;
          import java.io.File;
          import java.io.IOException;
          import java.util.List;
          import org.apache.commons.io.FileUtils;
          import org.openqa.selenium.By;
          import org.openqa.selenium.Keys;
          import org.openqa.selenium.OutputType;
          import org.openqa.selenium.TakesScreenshot;
          import org.openqa.selenium.WebDriver;
          //import org.openqa.selenium.WebDriver.Navigation;
          import org.openqa.selenium.WebElement;
          import org.openqa.selenium.chrome.ChromeDriver;
          import org.openqa.selenium.interactions.Actions;
          public class selenium  {
          public static void snapshot(TakesScreenshot drivername, String filename)
          {
          // this method will take screen shot ,require two parameters ,one is driver name, another is file name
          File scrFile = drivername.getScreenshotAs(OutputType.FILE);
          // Now you can do whatever you need to do with it, for example copy somewhere
          try {
          System.out.println("save snapshot path is:E:/"+filename);
          FileUtils.copyFile(scrFile, new File("E:\\"+filename));
          } catch (IOException e) {
          // TODO Auto-generated catch block
          System.out.println("Can't save screenshot");
          e.printStackTrace();
          }
          finally
          {
          System.out.println("screen shot finished");
          }
          }
          public static void main (String [] args) throws InterruptedException
          {
          String URL="http://61.135.169.105/";
          //avoid Chrome warnning message like "unsupported command-line flag --ignore-certificate-errors. "
          ChromeOptions options = new ChromeOptions();
          options.addArguments("--test-type");
          System.setProperty("webdriver.chrome.driver", "D:\\selenium\\chromedriver.exe");
          WebDriver driver = new ChromeDriver(options);
          driver.get(URL);
          //max size the browser
          driver.manage().window().maximize();
          /*
          Navigation navigation = driver.navigate();
          navigation.to(URL);*/
          Thread.sleep(2000);
          snapshot((TakesScreenshot)driver,"open_baidu.png");
          //WebElement reg=driver.findElement(By.name("tj_reg"));
          //reg.click();
          //    WebElement keyWord = driver.findElement(By.id("kw1"));
          //find the element
          WebElement keyWord = driver.findElement(By.xpath("http://input[@id='kw1']"));
          keyWord.clear();
          //send key words
          keyWord.sendKeys("小蘋果");
          Thread.sleep(3000);
          snapshot((TakesScreenshot)driver,"input_keyWord.png");
          WebElement submit = driver.findElement(By.id("su1"));
          System.out.println(submit.getLocation());
          submit.click();
          //System.out.println(driver.getWindowHandle());
          Thread.sleep(5000);
          WebElement se=driver.findElement(By.xpath("http://*[@id=\"2\"]/div[1]/div[2]/table/tbody/tr/td[5]/span/a")) ;
          Actions action = new Actions(driver);
          action.clickAndHold(se);
          action.sendKeys(Keys.DOWN);
          Thread.sleep(5000);
          List<WebElement> elementList = driver.findElements(By.tagName("herf"));
          for(WebElement e:elementList)
          {
          System.out.print("-->"+e.getText());
          }
          //se.click();
          // System.out.println(driver.getPageSource());
          //  System.out.println(pageSource);
          //WebElement link =driver.findElement(By.xpath(SELENIUM_LINK));
          WebElement link =driver.findElement(By.xpath("http://*[@id=\"2\"]/div[1]/div[2]/table/tbody/tr/td[5]/span/a"));//By.xpath("http://*[@id=\"1\"]/h3/a"));     //*[@id="1"]/h3/a
          link.click();
          Thread.sleep(5000);
          driver.switchTo().window(driver.getWindowHandles().toArray(new String[0])[1]);
          Thread.sleep(5000);
          WebElement down =driver.findElement(By.xpath("http://*[@id=\"128\"]"));//<i class="icon btn-icon-download-small"></i>
          Thread.sleep(5000);
          down.click();
          snapshot((TakesScreenshot)driver,"down_m.png");
          //get page title
          System.out.println(driver.getTitle());
          Thread.sleep(5000);
          WebElement userName=driver.findElement(By.id("TANGRAM__PSP_8__userName"));
          WebElement password=driver.findElement(By.id("TANGRAM__PSP_8__password"));
          WebElement login=driver.findElement(By.id("TANGRAM__PSP_8__submit"));
          Thread.sleep(5000);
          userName.sendKeys("QAtest");//your baidu userName
          password.sendKeys("mypassword");//your baidu password
          login.submit();
          //     navigation.back();
          snapshot((TakesScreenshot)driver,"open_bake.png");
          System.out.println(driver.getTitle()+"\n"+driver.getCurrentUrl());
          Thread.sleep(50000);
          File file=new File("C:\\Users\\Young\\Downloads\\小蘋果.mp3");
          if(file.exists())
          {
          System.out.println("PASS");
          }
          else
          {
          System.out.println("FAIL");
          }
          driver.quit();
          }
          }
            如果出現:unsupported command-line flag --ignore-certificate-errors.
            //avoid Chrome warnning message like "unsupported command-line flag --ignore-certificate-errors. "
            ChromeOptions options = new ChromeOptions();
            options.addArguments("--test-type");
            System.setProperty("webdriver.chrome.driver", "D:\\selenium\\chromedriver.exe");
            WebDriver driver = new ChromeDriver(options);
          English »
           

          posted @ 2014-09-18 09:28 順其自然EVO 閱讀(337) | 評論 (0)編輯 收藏

          各種測試方法的問題

          在 Exploratory Software Testig 一書中, James Whittaker在第二章中, 提到各種測試方法的不足:
            Defect Preventation
            從開發人員的角度來說, 他們希望藉由 design review, code review, static analysis tool, 和 unit test, 來增加軟件的質量.
            但是作者覺得這些方法都有些根本的問題:
            (1) 開發人員通常不是個好的測試人員
            - 開發人員想的是"如何才能實現這個功能", 而測試人員則是從"如何才能攻破這個功能" 來思考.
            - 因此開發人員會有盲點, 需要有另一組人從不同觀點來思考
            - 但是不代表開發人員不用作測試, 像是 formatting, data validation 和 error handling 等等都需要及時處理和驗證. 等到測試人員發現, 時間會花得很長, 也修正的代價也很高
            (2) 靜止狀態的程序不能完全代表真的測試目標
            - 有很多錯誤是和執行環境有關系, 通常在開發環境這些錯誤不會發生的
            (3) 缺乏客戶真正數據
            - 有些錯誤是和客戶真實的數據有關, 或者需要實行一段時間后, 累積效果出現后才會有問題
            - 可是開發人員通常沒有這些數據, 并且也沒有這么長的測試時間, 所以無法找出這類型的錯誤
            Defect Detection
            通常分成手動測試和自動化測試兩種:
            自動化測試
            - 測試人員不一定會是好的開發人員, 有些人可以, 有些人可能不行.
            - 測試程序也是會有 bug, 一旦出現后, 測試人員需要更多時間來除錯, 維護它的正確性和強固性. 所以你要花在測試的時間多, 還是應該花在維護測試程序的時間多?
            - 此外測試程序所在的執行環境, 以及所用的測試數據, 不是客戶的數據, 所以效果還是有限. 并且客戶可能也沒有勇氣, 讓你在他的 production 執行.
            - Oracle Problem 的問題是最難處理的, 也就是當你執行完測試時, 你無法確認是否真的實行正確. 像是 install 完畢, 甚么叫做 install 成功, 是所有 service 都啟動, 是所有檔案都復制完畢, 還是所有 registry 寫正確. 你可能無法列的出來, spec 也不會寫甚么叫做功能運作正確.
            手動測試
            - 也是由人來進行測試, 需要充分發聰明才智, 設計出真實客戶環境的數據和使用狀況. 尤其是有關 business logic 更是需要人腦介入.
            - 手動測試比自動化測試強的地方, 是因為現實狀況有太多不確定的因素, 有太多 scenario, 會導致測試程序員小的情況太多, 錯誤時都需要人腦介入, 一一來跟蹤.
            - 可是手動測試很慢, 無法反復使用, 測試步驟可能不一定有規律, 也不一定都能重復.

          posted @ 2014-09-18 09:26 順其自然EVO 閱讀(162) | 評論 (0)編輯 收藏

          性能測試URL自動轉碼

          English »
           
          最近做性能測試,寫了個python程序自動將URL里面的‘%2B’,‘20%’,‘3B'等轉換成正常字符,方便查看。
          import os,sys;
          path = sys.path[0]
          os.chdir(path)
          encode_list = 'encode_list.txt'
          result = path + '\\results'
          def get_encode():
          encode_file = open(path + '\\'+ encode_list)
          encode = dict()
          for line in encode_file:
          if line!='\n' and len(line) >1:
          if line.find('read me') <0:
          encode[line[1:].strip()] = line[0]
          return encode
          def get_files():
          files = os.listdir(path)
          file_list = list()
          for file in files:
          if file.endswith('.txt') and file!= encode_list:
          file_list.append(file)
          return file_list
          def relace_url_encode(strPri,dicEncode):
          items = dicEncode.items()
          for (key,value) in items:
          if strPri.find(key):
          strPri = strPri.replace(key,value)
          return strPri
          def create_result():
          if not os.path.isdir(result):
          os.makedirs(result)
          def write_result(filePri,strText):
          fp = open(result+'\\'+filePri,'w+')
          fp.write(strText)
          fp.close()
          create_result()
          encode = get_encode()
          file_list = get_files()
          for ff in file_list:
          try:
          f = open(ff)
          text = f.read()
          finally:
          f.close()
          temp = relace_url_encode(text,encode)
          temp = temp.replace('&','\n')
          write_result(ff,temp)
            下面是文件夾結構:
          encode.py的代碼貼在上面
            encode_list.txt里面裝的是轉換對照表,其中文件名是hard code在python程序里面的,最好不要改
            前面的是正常字符,后面的是需要轉換的字符
            需要轉碼的URL形如下面的形式:
          selectForm=selectForm&publishId=iphone6SapptR&color=%7B%22colorDisplayText%22%3A%22Grey%22%2C%22colorId%22%3A%22Grey%22%2C%22publishId%22%3A%22iphone6SapptM%22%2C%22modelCode%22%3A%22iphone6SmodelM%22%2C%22available%22%3Atrue%7D&rom=%7B%22capacityDisplayText%22%3A%2216GB%22%2C%22capacityId%22%3A%2216GB%22%2C%22imageFileName%22%3A%22iPhoneX-gold.png%22%2C%22publishId%22%3A%22iphone6SapptM%22%2C%22modelCode%22%3A%22iphone6SmodelM%22%2C%22available%22%3Atrue%7D&locationId=%7B%22locationId%22%3A%22Marina_Bay_Sands_Exhibition_Hall_A%22%2C%22locationDisplayText%22%3A%22Marina%20Bay%20Sands%20Exhibition%20Hall%20A%22%2C%22publishId%22%3A%22iphone6SapptM%22%2C%22available%22%3Atrue%7D&dateId=&timeId=&javax.faces.ViewState=H4sIAAAAAAAAAE1QO0sDQRAeL7n4RGIEK9PZWLhgJ1hoQIOH8YGgCBa6uVuTC3e76z5ydxaBNFrYWGhhIVpY5k%2BIhZ2gpZXYW9u6F0LiBzvMst9jZjs%2FYHMpYKqBmxhp5QdoA8v6Fub28Ofzy8zJewasMowFDHtl7ComHBhVdUFknQVezFdWIcVENGJq3hxLwazLQiQ1RafYJRKtJRSHvltylc%2BoNFnTg6ySEDip%2BFLF7Y%2Fi3Su%2Bz8CQA1npn5OYp8ZRNq2xArtx7HuL2qiPKl19gGkN7VQ
          bxFXL12%2BHD3k5H1iGmsosfQYtyJnO5gb9W6YlYCFVx73ZzKScUUIV2ncOfBLtMabmdgXjRKhkkyQSeigYZwGTg%2BR1qsP%2Fj1xBLsBSOV7%2FN7s8hypSI6Lw%2Ffj0275cstL97CYONDF%2B%2BQFvW4dVIi46t8Xxm6%2Br%2FiKcx3%2FTRn8XowEAAA%3D%3D&javax.faces.source=color%3A1&javax.faces.partial.event=change&javax.faces.partial.execute=color%20color%3A1&javax.faces.partial.render=productImage%20rom%20timeId%20dateId%20locationId&javax.faces.behavior.event=change&javax.faces.partial.ajax=true
            我把轉碼過的結果全部放在result文件夾里面,雙擊運行,所有的txt文件都會被轉碼。并且該文件夾隨便放在哪里,代碼均可以執行。
            轉碼過后:
          selectForm=selectForm
          publishId=iphone6SapptR
          color={"colorDisplayText":"Grey","colorId":"Grey","publishId":"iphone6SapptM","modelCode":"iphone6SmodelM","available":true}
          rom={"capacityDisplayText":"16GB","capacityId":"16GB","imageFileName":"iPhoneX-gold.png","publishId":"iphone6SapptM","modelCode":"iphone6SmodelM","available":true}
          locationId={"locationId":"Marina_Bay_Sands_Exhibition_Hall_A","locationDisplayText":"Marina_Bay_Sands_Exhibition_Hall_A","publishId":"iphone6SapptM","available":true}
          dateId=
          timeId=
          javax.faces.ViewState=H4sIAAAAAAAAAE1QO0sDQRAeL7n4RGIEK9PZWLhgJ1hoQIOH8YGgCBa6uVuTC3e76z5ydxaBNFrYWGhhIVpY5k+IhZ2gpZXYW9u6F0LiBzvMst9jZjs/YHMpYKqBmxhp5QdoA8v6Fub28Ofzy8zJewasMowFDHtl7ComHBhVdUFknQVezFdWIcVENGJq3hxLwazLQiQ1RafYJRKtJRSHvltylc+oNFnTg6ySEDip+FLF7Y/i3Su+z8CQA1npn5OYp8ZRNq2xArtx7HuL2qiPKl19gGkN7VQ
          bxFXL12+HD3k5H1iGmsosfQYtyJnO5gb9W6YlYCFVx73ZzKScUUIV2ncOfBLtMabmdgXjRKhkkyQSeigYZwGTg+R1qsP/j1xBLsBSOV7/N7s8hypSI6Lw/fj0275cstL97CYONDF++QFvW4dVIi46t8Xxm6+r/iKcx3/TRn8XowEAAA==
          javax.faces.source=color:1
          javax.faces.partial.event=change
          javax.faces.partial.execute=color_color:1
          javax.faces.partial.render=productImage_rom_timeId_dateId_locationId
          javax.faces.behavior.event=change
          javax.faces.partial.ajax=true
            轉換后就可以更方便的查找對比,方便測試進行。
            應該還有需要改進的地方,如果測試需要,再做改進。

          posted @ 2014-09-18 09:23 順其自然EVO 閱讀(524) | 評論 (0)編輯 收藏

          Linux-shell獲取天氣

          Linux中的shell獲取天氣,本來覺的比較難,原來,真簡單,個位數的代碼就搞定。
            1獲取對應城市天氣
            所有天氣信息都從中國天氣網獲取。每一個城市多會對應一個id(比如,北京為101010100,因為本人在銀川,所以例子中就用銀川的id:101170101),通過id就可以獲取對應城市實時天氣或者全天天氣,還可以獲取七天天氣。
            1.1shell腳本
            shell腳本代碼如下:
          #!/bin/sh
          weatherDateRoot=http://www.weather.com.cn/data/sk/101170101.html
          weatherDataFile=weather.html
          wget $weatherDateRoot -O $weatherDataFile > /dev/null 2>&1
          sed 's/.*temp":"\([0-9]\{1,2\}\).*/\1/g' $weatherDataFile
            此腳本通過將天氣信息獲取,然后通過正則匹配到當前溫度。
            如果你只用這個腳本,不再進行二次處理,那也太麻煩。我獲取天氣信息后是顯示到終端命令提示符中的,所以需要還要在做處理。
            2終端命令提示符中顯示天氣
            首先獲取對應城市天氣,如銀川對應的實時天氣信息在:
            http://www.weather.com.cn/data/sk/101170101.html
            你先在中國天氣網搜索到你想要的城市的天氣,網址中會包含城市天氣id,將上面的網址中的id替換成你城市的id就可以獲取。
            還有全天天氣信息:
            http://www.weather.com.cn/data/cityinfo/101170101.html
            不知道中國天氣網提供七天天氣信息沒有?如果有,那么我們也可以通過此方法獲取七天天氣信息。
            2.1獲取天氣信息
            對應shell腳本:
          #!/bin/sh
          allDataUrl=http://www.weather.com.cn/data/cityinfo/101170101.html
          allDataFile=/home/snowsolf/shell/weather/allDay.html
          dataUrl=http://www.weather.com.cn/data/sk/101170101.html
          dataFile=/home/snowsolf/shell/weather/weather.html
          wget $dataUrl -O $dataFile > /dev/null 2>&1
          wget $allDataUrl -O $allDataFile > /dev/null 2>&1
            2.2定時獲取
            通過crontab命令設置定時任務,執行crontab -e命令(如果第一次需要設置默認編輯器),然后在文件末尾添加:
            */30 * * * * /home/snowsolf/shell/weather/weather.sh >> /dev/null
            此行代碼設置每30分鐘執行一次獲取天氣的腳本,具體crontab命令其它語法可以google或baidu。
            2.3提取天氣
            sed 's/.*temp":"\([0-9]\{1,2\}\).*/\1/g'
            此命令可以從獲取的實時天氣文件中獲取實時天氣。
            2.4終端命令提示符中顯示
            你可以參考http://www.cnblogs.com/snowsolf/p/3192224.html。這里可以讓你的命令提示符更絢麗。
            最后上一幅我的命令提示符圖:

          posted @ 2014-09-17 10:08 順其自然EVO 閱讀(262) | 評論 (0)編輯 收藏

          什么是數據庫歸檔

          如果您的日常工作中需要對數據庫進行管理,那您肯定已經或即將遭遇這樣的困惑:隨著業務的蓬勃發展,數據庫文件的大小逐漸增大,您需要為在線業務提供越來越大的高性能磁盤容量,但數據庫的工作性能卻日漸變差。如何解決這樣的問題呢?一種新興的技術——數據庫歸檔也許能夠幫您的忙。
            數據庫歸檔技術是一種保持在線數據庫規模大體不變卻有能夠為用戶應用提供穩定的數據庫性能的方法。其工作原理是,將數據庫中不經常使用的數據遷移至近線設備,將長期不使用的數據遷移至文件形式歸檔。這樣,隨著應用的需要,數據會在在線、近線和文件文檔之間移動,如當應用需要訪問很久以前的某些數據,它們的物理位置在近線設備,則會自動移動到在線設備。對用戶的應用而言,這些都是透明的,就像所有數據都存放在在線設備一樣,不會對數據庫應用產生任何影響。
            數據庫歸檔把信息生命周期管理的概念引入到應用程序數據管理中,可以監控、分析和預測數據量的增加,利用在線的數據庫隨時識別并定位不活動的數據或已經完成的業務交易,把長期不用的數據封裝歸檔,這樣就大幅降低了活動數據的規模,數據庫等應用程序運行時的效率可以大幅提升。經過歸檔,即使在應用程序本身已經廢棄的時候還能夠重新利用其數據,同時保持實時訪問已歸檔數據的能力。
            需要指出的是,數據庫歸檔與文件歸檔并不相同。按照SNIA(存儲網絡工業協會)的定義,歸檔是數據集合的一致性拷貝,通常用以長期持久地保存事務或者應用狀態記錄。一般情況下,歸檔通常用以審計和分析的目的,而不是用于應用恢復。歸檔之后,文件的原件一般會被刪除,并且需要通過前臺的操作來恢復文件。普通的文件歸檔只能夠對文件進行操作,而且歸檔后的文件一般不再產生變化。而數據庫歸檔則不同,數據在歸檔之后仍然存在改變的可能,也隨時會變成在線的活動數據。
            現已經被HP公司收購的OuterBay公司就是數據庫歸檔領域的一個著名廠商,其提供的數據庫歸檔產品主要有三種:Relocator產品進行在線數據歸檔,打包歸檔產品將數據庫歸檔成為文件(.XSD或者.XML格式),子集拷貝產品為用戶提供用于測試的數據庫拷貝。OuterBay有兩個主要的競爭對手,Princeton Softech和Applimation。前者產品主要針對大型機系統設計,而且產生的文件是專有格式;后者公司規模較小,其產品也可有效識別出數據庫中訪問頻率較低的數據,并將其移出數據庫,存入在線的歷史數據庫中。
            事實上,所有的數據庫廠商都提供了類似的數據庫歸檔功能,但目前沒有形成商用產品,用戶可以使用命令或者編程進行相關操作。數據庫歸檔的概念本身十分簡單,把一條記錄從生產數據庫插入到歷史數據庫中,然后把該條記錄在生產數據庫中刪除就實現了數據庫歸檔的功能。
            但是在線數據庫需要高可靠性、錯誤處理、審計以及異常處理(如斷電、數據庫崩潰)等高級功能,這些都只能由專業的數據庫歸檔產品提供。
            值得指出的是,數據庫歸檔對管理員的日常備份工作很有幫助。如果沒有進行數據庫歸檔,那么不僅需要備份整個大型的數據庫,而且備份窗口要求很長。在進行數據歸檔之后,由于已歸檔的數據庫部分可以隨時進行備份而不會影響在線數據庫的應用,這部分數據庫一般為長時間不活躍的數據,因此備份工作很容易完成,而在線的數據庫部分也因為進行數據庫歸檔后而瘦身,備份數據量減少,備份窗口減小,從而整體減少了需要備份的數據總量。而對數據進行恢復的時候,可以在短時間內首先完成在線數據庫恢復,之后在在線數據庫工作的同時進行其他數據的恢復工作。

          posted @ 2014-09-17 10:07 順其自然EVO 閱讀(355) | 評論 (0)編輯 收藏

          Java圖片上查找圖片算法

           之前用按鍵精靈寫過一些游戲輔助,里面有個函數叫FindPic,就上在屏幕范圍查找給定的一張圖片,返回查找到的坐標位置。
            現在,Java來實現這個函數類似的功能。
            算法描述:
            屏幕截圖,得到圖A,(查找的目標圖片為圖B);
            遍歷圖A的像素點,根據圖B的尺寸,得到圖B四個角映射到圖A上的四個點;
            得到的四個點與圖B的四個角像素點的值比較。如果四個點一樣,執行步驟4;否則,回到步驟2繼續;
            進一步對比,將映射范圍內的全部點與圖B全部的點比較。如果全部一樣,則說明圖片已找到;否則,回到步驟2繼續;
            這里,像素之間的比較是通過BufferedImage對象獲取每個像素的RGB值來比較的。如下,將BufferedImage轉換為int二維數組:
          1     /**
          2      * 根據BufferedImage獲取圖片RGB數組
          3      * @param bfImage
          4      * @return
          5      */
          6     public static int[][] getImageGRB(BufferedImage bfImage) {
          7         int width = bfImage.getWidth();
          8         int height = bfImage.getHeight();
          9         int[][] result = new int[height][width];
          10         for (int h = 0; h < height; h++) {
          11             for (int w = 0; w < width; w++) {
          12                 //使用getRGB(w, h)獲取該點的顏色值是ARGB,而在實際應用中使用的是RGB,所以需要將ARGB轉化成RGB,即bufImg.getRGB(w, h) & 0xFFFFFF。
          13                 result[h][w] = bfImage.getRGB(w, h) & 0xFFFFFF;
          14             }
          15         }
          16         return result;
          17     }
            比較兩個像素點的RGB值是否相同,是通過異或操作比較的(據說比==效率更高),如果異或操作后得到的值為0,說明兩個像素點的RGB一樣,否則不一樣。
          下面附上算法完整java代碼:
          1 package com.jebysun.test.imagefind;
          2
          3 import java.awt.AWTException;
          4 import java.awt.Rectangle;
          5 import java.awt.Robot;
          6 import java.awt.Toolkit;
          7 import java.awt.image.BufferedImage;
          8 import java.io.File;
          9 import java.io.IOException;
          10
          11 import javax.imageio.ImageIO;
          12 /**
          13  * 屏幕上查找指定圖片
          14  * @author Jeby Sun
          15  * @date 2014-09-13
          16  * @website http://www.jebysun.com
          17  */
          18 public class ImageFindDemo {
          19
          20     BufferedImage screenShotImage;    //屏幕截圖
          21     BufferedImage keyImage;           //查找目標圖片
          22
          23     int scrShotImgWidth;              //屏幕截圖寬度
          24     int scrShotImgHeight;             //屏幕截圖高度
          25
          26     int keyImgWidth;                  //查找目標圖片寬度
          27     int keyImgHeight;                 //查找目標圖片高度
          28
          29     int[][] screenShotImageRGBData;   //屏幕截圖RGB數據
          30     int[][] keyImageRGBData;          //查找目標圖片RGB數據
          31
          32     int[][][] findImgData;            //查找結果,目標圖標位于屏幕截圖上的坐標數據
          33
          34
          35     public ImageFindDemo(String keyImagePath) {
          36         screenShotImage = this.getFullScreenShot();
          37         keyImage = this.getBfImageFromPath(keyImagePath);
          38         screenShotImageRGBData = this.getImageGRB(screenShotImage);
          39         keyImageRGBData = this.getImageGRB(keyImage);
          40         scrShotImgWidth = screenShotImage.getWidth();
          41         scrShotImgHeight = screenShotImage.getHeight();
          42         keyImgWidth = keyImage.getWidth();
          43         keyImgHeight = keyImage.getHeight();
          44
          45         //開始查找
          46         this.findImage();
          47
          48     }
          49
          50     /**
          51      * 全屏截圖
          52      * @return 返回BufferedImage
          53      */
          54     public BufferedImage getFullScreenShot() {
          55         BufferedImage bfImage = null;
          56         int width = (int) Toolkit.getDefaultToolkit().getScreenSize().getWidth();
          57         int height = (int) Toolkit.getDefaultToolkit().getScreenSize().getHeight();
          58         try {
          59             Robot robot = new Robot();
          60             bfImage = robot.createScreenCapture(new Rectangle(0, 0, width, height));
          61         } catch (AWTException e) {
          62             e.printStackTrace();
          63         }
          64         return bfImage;
          65     }
          66
          67     /**
          68      * 從本地文件讀取目標圖片
          69      * @param keyImagePath - 圖片絕對路徑
          70      * @return 本地圖片的BufferedImage對象
          71      */
          72     public BufferedImage getBfImageFromPath(String keyImagePath) {
          73         BufferedImage bfImage = null;
          74         try {
          75             bfImage = ImageIO.read(new File(keyImagePath));
          76         } catch (IOException e) {
          77             e.printStackTrace();
          78         }
          79         return bfImage;
          80     }
          81
          82     /**
          83      * 根據BufferedImage獲取圖片RGB數組
          84      * @param bfImage
          85      * @return
          86      */
          87     public int[][] getImageGRB(BufferedImage bfImage) {
          88         int width = bfImage.getWidth();
          89         int height = bfImage.getHeight();
          90         int[][] result = new int[height][width];
          91         for (int h = 0; h < height; h++) {
          92             for (int w = 0; w < width; w++) {
          93                 //使用getRGB(w, h)獲取該點的顏色值是ARGB,而在實際應用中使用的是RGB,所以需要將ARGB轉化成RGB,即bufImg.getRGB(w, h) & 0xFFFFFF。
          94                 result[h][w] = bfImage.getRGB(w, h) & 0xFFFFFF;
          95             }
          96         }
          97         return result;
          98     }
          99
          100
          101     /**
          102      * 查找圖片
          103      */
          104     public void findImage() {
          105         findImgData = new int[keyImgHeight][keyImgWidth][2];
          106         //遍歷屏幕截圖像素點數據
          107         for(int y=0; y<scrShotImgHeight-keyImgHeight; y++) {
          108             for(int x=0; x<scrShotImgWidth-keyImgWidth; x++) {
          109                 //根據目標圖的尺寸,得到目標圖四個角映射到屏幕截圖上的四個點,
          110                 //判斷截圖上對應的四個點與圖B的四個角像素點的值是否相同,
          111                 //如果相同就將屏幕截圖上映射范圍內的所有的點與目標圖的所有的點進行比較。
          112                 if((keyImageRGBData[0][0]^screenShotImageRGBData[y][x])==0
          113                         && (keyImageRGBData[0][keyImgWidth-1]^screenShotImageRGBData[y][x+keyImgWidth-1])==0
          114                         && (keyImageRGBData[keyImgHeight-1][keyImgWidth-1]^screenShotImageRGBData[y+keyImgHeight-1][x+keyImgWidth-1])==0
          115                         && (keyImageRGBData[keyImgHeight-1][0]^screenShotImageRGBData[y+keyImgHeight-1][x])==0) {
          116
          117                     boolean isFinded = isMatchAll(y, x);
          118                     //如果比較結果完全相同,則說明圖片找到,填充查找到的位置坐標數據到查找結果數組。
          119                     if(isFinded) {
          120                         for(int h=0; h<keyImgHeight; h++) {
          121                             for(int w=0; w<keyImgWidth; w++) {
          122                                 findImgData[h][w][0] = y+h;
          123                                 findImgData[h][w][1] = x+w;
          124                             }
          125                         }
          126                         return;
          127                     }
          128                 }
          129             }
          130         }
          131     }
          132
          133     /**
          134      * 判斷屏幕截圖上目標圖映射范圍內的全部點是否全部和小圖的點一一對應。
          135      * @param y - 與目標圖左上角像素點想匹配的屏幕截圖y坐標
          136      * @param x - 與目標圖左上角像素點想匹配的屏幕截圖x坐標
          137      * @return
          138      */
          139     public boolean isMatchAll(int y, int x) {
          140         int biggerY = 0;
          141         int biggerX = 0;
          142         int xor = 0;
          143         for(int smallerY=0; smallerY<keyImgHeight; smallerY++) {
          144             biggerY = y+smallerY;
          145             for(int smallerX=0; smallerX<keyImgWidth; smallerX++) {
          146                 biggerX = x+smallerX;
          147                 if(biggerY>=scrShotImgHeight || biggerX>=scrShotImgWidth) {
          148                     return false;
          149                 }
          150                 xor = keyImageRGBData[smallerY][smallerX]^screenShotImageRGBData[biggerY][biggerX];
          151                 if(xor!=0) {
          152                     return false;
          153                 }
          154             }
          155             biggerX = x;
          156         }
          157         return true;
          158     }
          159
          160     /**
          161      * 輸出查找到的坐標數據
          162      */
          163     private void printFindData() {
          164         for(int y=0; y<keyImgHeight; y++) {
          165             for(int x=0; x<keyImgWidth; x++) {
          166                 System.out.print("("+this.findImgData[y][x][0]+", "+this.findImgData[y][x][1]+")");
          167             }
          168             System.out.println();
          169         }
          170     }
          171
          172
          173     public static void main(String[] args) {
          174         String keyImagePath = "D:/key.png";
          175         ImageFindDemo demo = new ImageFindDemo(keyImagePath);
          176         demo.printFindData();
          177     }
          178
          179 }
            這種算法是精確比較,只要有一個像素點有差異,就會找不到圖片。當然,如果想指定一個比較的精確度,我也有個思路,就是在算法步驟4比較映射范圍內全部像素點的時候做個統計,如果90%的點都相同,那就是說精確度是0.9。
            另外,可能還要考慮效率問題,不過,我在我的應用場景中并不太在意效率。如果有朋友看到這篇文章,對這個話題有更好的想法,請留言。

          posted @ 2014-09-17 09:52 順其自然EVO 閱讀(727) | 評論 (0)編輯 收藏

          僅列出標題
          共394頁: First 上一頁 45 46 47 48 49 50 51 52 53 下一頁 Last 
          <2025年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          導航

          統計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 额济纳旗| 鹤岗市| 武冈市| 辽宁省| 恩平市| 安达市| 鹤岗市| 玉溪市| 手游| 湖南省| 南涧| 新乡县| 张北县| 安义县| 罗定市| 庆城县| 滦平县| 宝坻区| 永泰县| 霞浦县| 许昌市| 大邑县| 黑水县| 上思县| 陆川县| 平远县| 南昌县| 绍兴县| 左云县| 子洲县| 万盛区| 铜陵市| 清徐县| 巫溪县| 鄂托克前旗| 尤溪县| 宜兰县| 淳安县| 永仁县| 侯马市| 敦化市|