需求是整個軟件項目最關鍵的一個輸入,據統計,不成功的項目中有37%的問題是由需求造成的。和傳統的硬件生產企業相比較,軟件的需求具有模糊性、不確定性、變化性和主觀性的特點,在硬件生產企業中,產品的需求是明確的、有形的、客觀的、可描述的、可檢測的,而軟件需求不具備此特征。需求文檔作為客戶和開發人員、開發人員之間進行交互的文檔,它將系統的需求進行了“固化”,是需求的載體,其作用是至關重要的。筆者結合多年的企業管理信息系統的開發經驗,總結了如下的需求描述的方法與經驗,供各位同行參考。
1 構成企業管理信息系統的5個基本要素
對企業需求的描述可以從2個方面來進行描述,一個方面是對客戶現行系統的描述,一個方面是對系統未來的設想。總的而言,無論是從那個方面來描述,構成企業信息系統主要包括5個基本要素:企業的組織結構、流程、數據、商務規則與功能(性能)。其中從用戶的角度主要關注流程,是以流程為核心的,通過流程將其他幾個要素貫穿起來,需求分析人員也應該從這個角度來和用戶溝通;從開發者的角度主要關注企業的數據、商務規則與功能,以便于系統的實現;從實施者的角度主要關注企業的組織結構與功能,以便于系統的發布與實施。
(1) 企業的組織模型
即企業的組織結構關系,包括部門設置、崗位設置、崗位職責等。樹型組織結構圖是描述企業的組織模型的一種常用方法,它可用來搞清各部門之間的領導關系,每個部門內部的人員配備情況, 職責分工等情況,它是劃分系統范圍,進行系統網絡規劃的基礎。在組織結構圖中應將用戶的組織結構逐層詳細描述,每個部門的職責也應進行簡單的描述。組織結構是用戶企業業務流程與信息的載體,對分析人員理解企業的業務、確定系統范圍具有很好的幫助。取得用戶的組織結構圖,是需求獲取步驟中的基礎
工作之一。
用戶環境中的企業崗位或角色,和組織機構一樣,也是分析人員理解企業業務的基礎,也是分析人員提取對象的基礎。
對用戶角色的識別常常遺漏的是計算機系統的系統管理人員,角色識別不全,對以后的功能識別會造成盲區。
(2) 企業的流程模型
即企業的業務流程,包含哪些流程、流程之間的關系、每個流程中包括哪些活動、每個活動涉及到的崗位。企業的作業流程首先要有一個總的業務流程圖,將企業中各種業務之間的關系描述出來,然后對每種業務進行詳細的描述,使業務流程與部門職責結合起來。詳細業務流程圖可以采用直式業務流程圖形式。對企業而言需要定義關于業務流程圖的描述標準,大家采用相同的圖例來描述,便于管理。
業務流程圖的優點 :
■繪圖的過程,實際上是作業流程條理化的過程
■表達形象直觀,易于和用戶交流,易于項目組內部交流調研的結果,需要得到用戶的認同,這就需要和用戶交流調研的結果,交流的文檔要通俗、易懂, 不能采用專業術語。
■可以作為培訓實施人員與技術服務人員的文檔
業務流程圖的缺點 :
■對高層管理人員的實際需求調查的不清楚.
這一方面是由于用戶沒有接觸過計算機, 對采用計算機后的管理會是什么樣子?計算機能夠完成當前手工操作的哪些內容?能夠作哪些現在手工無法完成的工作等等沒有清楚的概念,因此用戶無法將這些問題反應出來. 另一方面說明分析人員沒有經驗,對原始材料挖掘不深,不能從用戶 提供的材料中提煉處來用戶的真正需求,不能找到當前管理中的問題。
■對各種業務之間的總體關系沒有表達出來.
采用直式業務流程圖可以將企業的每一種業務的處理流程清楚地表達出來, 但是各業務之間的聯系卻沒有表示出來,單看一種業務的流程圖很清楚,但是卻不能綜合在一起,沒有整體的概念,作為需求分析的文檔,在這方面表達的不夠完整。
■在不利用工具的情況下,畫法煩瑣。
圖形可以將流程描述的很清楚,但是還要附加以一些文字說明,如關于業務發生的頻率、意外事故的處理、高峰期的業務頻率等,不能在流程圖中描述出的內容,需要用文字進行詳細描述。
(3) 企業的數據模型
即企業中的信息載體有哪些?以及對這些信息載體的詳細刻畫,包括企業的各種單據、帳本、報表的描述。在需求報告中,應該將單據的描述格式化,需要描述的內容包括:
單據的用途,即單據用在什么地方?
單據的格式:需要明確的畫出來,并有實際的有數據的樣例,能夠具體直觀地說明問題;
單據中的數據項的具體描述:長度、類型、計算生成方法、約束條件等;
單據的數據項是由哪些不同類型的角色來填寫地,包括用計算機可以填那些數據項。
單據中哪些數據是必填的,哪些是可以不用填的。
單據流量:平均每天產生多少條記錄,高峰期的數量;
單據的分類:可以從多個角度上進行分類,如:按業務類型來分類(采購/銷售/生產),按生成的方式來分類(手工錄入型/自動生成型),按格式變化的頻繁程度來分類(易變型/穩定型),按表現形式來分類(列表型/卡片型)等等。
單據之間的關系:引用關系等等。
同樣對于需要的報表與帳本也可以參照上面的條目進行詳細的刻畫。
(4) 企業的商務規則模型
即企業中的商務規則有哪些?這些規則用在哪些地方? 商務規則可以從影響的范圍劃分為2類:一類是局部的規則,如不允許出現負庫存,一類是整體的規則,如對所有的物料管理到批次。商務規則一般是隱藏在功能模型或者流程模型中,不需要單獨描述,但是有些復雜的商務規則是需要單獨抽取出來描述,如企業的各種單據記帳的商務邏輯,5)企業的功能模型
功能需求是用戶的最主要的需求,對用戶功能需求的描述可以采用文字描述也可以采用語言加圖形的描述方式,只要能夠將用戶的需求描述地完整、準確、易于理解即可。對功能需求比較復雜的系統(如超過10個功能項),可以先描述一個概要,對簡單的系統可以直接進行詳細描述。對于用戶的功能需求要進行分類,分類的方法應便于用戶理解,如按照用戶的部門設置情況,進行描述每個部門的需求,這樣也便于組織用戶進行評審。以下是分類方法的舉例:
按部門分類:如采購科、銷售科、計劃科、生產車間、財務科、統計科、總經理等;
按功能類型分類:如單據錄入、單據審核、單據查詢、記帳、帳本查詢、統計報表、系統維護等。
對功能需求的分類在不同的層次可以采用不同的方法。
對每一項功能應有一個功能編號,以便于與功能規格說明書中的章節進行對應。對每一項功能的描述,應指明用戶的輸入(input)、處理方法(process)、系統的輸出(output)及對此項功能的其他要求。功能需求還應注明使用此功能的崗位。對系統管理員要求的特殊功能可以在此注明,非特殊要求可以在需求分析規格說明書中詳細論述。如用戶權限可分級,要有操作日志等。
功能需求與性能需求是密不可分的,籠統的性能需求沒有任何意思,必須具體到某項功能需求上來,這是分析人員在分析系統時容易忽略的一項。
對上述的5個基本元素可以將他們描述為一個五元組〈組織,流程,功能,數據,業務邏 輯〉,對于用戶來講,他們習慣于從組織維來看待系統,即某個部門有哪些崗位,每個崗位參與了哪些流程的哪些活動(功能),在某個功能上操作了哪些數據,對這些數據進行了哪些邏輯處理;對于開發人員習慣于從功能維來看待系統,即某個功能操作了哪些數據,對這些數據進行了哪些邏輯處理,這個功能屬于哪個流程,可以由哪些崗位來使用;對于設計人員可能習慣于從數據維來看待系統:即系統中有哪些數據,在這些數據上可以做哪些處理,這些處理用OO的思想來看即是對數據對象的操作。
對以上的5個基本元素進行描述實際上就是系統建模的過程,為確保模型的可操作性,除了上面的5個基本要素外,還需要重點描述的內容有:
(1) 新系統對應用模式帶來的變化
包括對企業的組織結構、作業流程、單據帳本報表等的格式、商務規則等的改變。
(2) 新系統的界面模型
用開發工具將用戶操作界面快速畫出來,使用戶心中有數。若時間允許,可將界面原型與數據庫表、字段連接起來,真正做出系統雛形,即快速原型法。
2 閱讀需求文檔的4類讀者
需求報告的最終目的是給人來閱讀的,所以一定要考慮需求報告的讀者群,有4類角色可能閱讀企業管理系統的需求文檔:
客戶與用戶業務高層;
用戶的中層管理人員與具體人員;
用戶IT主管與開發人員,包括設計人員、編碼人員、同行的專家;
項目管理人員:包括項目經理、質量保證人員、測試人員、需求管理員、配置管理員、計劃人員等等;
不同的讀者對文檔的閱讀需求是不同的,他們關注的信息是不同的。我見過了很多次需求評審的失敗(如果做好需求評審我會另外再撰文描述),總結下來我認為和需求描述沒有區分讀者群是很有關系的。針對上述的4種分類,我們具體的來分析一下每類讀者的特點:
(1) 客戶與用戶業務高層
他們關心的企業是系統的目標性需求,關心的是系統總體的功能框架,關心的是系統解決了哪些管理問題,對具體的需求是不關心的,所以給他們閱讀的文檔應該是從總體上來描述,要高度抽象。由于他們的工作很忙,很難有比較長的時間來讀這些材料,所以要簡短明了,能夠用1頁紙說明問題的就要不要用2頁紙,而且一般都要給高層進行需求匯報,需要配上語言說明,因此采用PowerPiont片子也就成了一種常用的方法,講解需求與討論一般應掌握不要超過1小時。需求人員常犯的毛病是過多地關注了企業的細節性需求,而忽略系統的目標性需求,所以在安排需求獲取的步驟上、需求報告的編寫上往往沒有抓住企業高層最關心的問題、沒有抓住根本性的問題,在給企業的高層匯報時當然很難通過評審。
(2)用戶的中層管理人員與具體人員
企業的中層管理人員關注的是企業的局部需求,他們要求對自己的負責的局部系統能夠有總體的了解,能夠和其他的子系統銜接的很好,業務流程很流暢,覆蓋了自己需要的所有業務流程,能夠通過系統起到控制作用就行了。具體的操作人員更關心自己的的哪些活動是否在系統中都能處理,軟件是否可以很容易地操作,他們關注的焦點更具體,要求更直觀。所以對這類的讀者可以通過比較詳細的文檔來描述需求了,當然應該以他們習慣的思維方式來描述,不能從開發人員的角度來描述。我看到過很多幾百頁的需求文檔給用戶去閱讀、去評審,結果要么用戶不置可否,要么直接講看不懂,為什么呢?一是開發人員在文檔中分子系統、分模塊、分功能點一層深入下去描述,不符合用戶的思維習慣,他們希望能夠從業務流程、業務活動的角度來考慮問題,而不是功能;二是太多了,用戶也沒有時間靜下心來去消化、吸收如此多的文檔,需求畢竟不是小說,能夠那么吸引讀者。
(3)用戶IT主管與開發人員,包括設計人員、編碼人員、同行的專家
大多數分析人員可能最擅長的就是些寫這類的文檔了,往往也是那這類的文檔給所有的讀者看,其問題我們上邊都說了,這里我們就不贅述了。
需要注意的是在描述需求時候傳統的做法是以功能為主線,來展開描述,實際上如果是以數據為主線來描述需求也是一種很好的辦法,在我們上面談到的五元組中,從數據的角度來分析系統可以更容易實現向OOA、OOD的切換。
(4) 項目管理人員:包括項目經理、質量保證人員、測試人員、需求管理員、配置管理員、計劃人員等等
把拿給開發人員看的需求文檔給管理人員看,這也是分析人員常犯的毛病。管理人員實際上最關心的是需求列表。
在此基礎上項目經理、質量保證人員可以據此來進入項目策劃過程,測試人員可據此進入測試策劃過程,需求管理員、配置管理員可以識別配置項制定相關的活動計劃。沒有這張表管理人員就很難高效地開展他們的管理活動,也就談不到最基本的需求復用了。在上述的表中,需求的優先級是很重要的一列,對項目經理進行項目管理的平衡決策是很重要的,實際上需求的優先級可能比需求本身更重要。
3 需求描述的表示技巧
上面我們談到了,需求文檔是人與人之間交互的文檔,是不同類型的人之間交互的文檔,因此需求文檔的可讀性是一個很重要的方面,為了提高文檔的可讀性可以借鑒下面的一些做法:
在文檔的描述中,適當運用鏈接,增強文檔的可讀性;
多用窮舉的方式,以便于發現遺漏的需求;
通過適當的換行來提高可讀性 ;
采用黑體、斜體、下劃線、顏色等多種方式來突出重要內容;
定義標準的術語,以減少二義性,減少文檔的頁數;
在功能需求的描述中,對于類似的、統一的功能可以單獨地進行詳細描述,其他地方進行引用,或做為術語進行定義,以簡化文檔,減少重復。如;
· 錄入功能
· 打印功能
· 條件查詢功能
· 排序功能等等
結 語
盡管你按照上述的方法去做了,也不要期望能夠編寫出一份能體現需求應具備的所有特性的文檔,無論你如何去細化、分析、評論和優化需求,都不可能達到完美,但是你能夠做到“可接受”,寫一份客戶、用戶、開發人員、管理人員都認可的一份需求,而不是完美的需求
大家做
Android開發,看到別人應用里一些好的功能,是不是很想得到源碼,借鑒一下?既然Android是用JAVA開發的,那么我們就能很容易的通過反編譯的到應用的源代碼。下面我簡單介紹下應該怎么操作。
具體步驟:
1.首先將apk文件后綴改為RAR并解壓。
2.解壓rar文件,得到其中的classes.dex文件
3.我們需要用到dex2jar 把dex文件轉為jar文件。
3.1把解壓得到的classes.dex文件放入dex2jar.bat 所在目錄 )
3.2打開DOS命令行,進入dex2jar所在目錄,運行 dex2jar.bat classes.dex 生成 classes_dex2jar.jar
1.對于注解類的支持
2.測試參數化
對于TestNG和JUnit都有不同的參數化設定功能,可是表現形式不同。參數化設定意味著當運行
測試的時候,參數可以從外部傳入到這個測試單元中。
對于TestNG來說,要2部分,一是當構造測試代碼時候,要用@Parameters(value=XXX)來表示這個測試需要什么參數,這樣的優點是可以讓測試更加靈活并且可以根據不同的參數來得到不同的期望結果,比如:
public class TestNGTest6_1_0 { @Test @Parameters(value="number") public void parameterIntTest(int number) { System.out.println("Parameterized Number is : " + number); } } |
然后,在我們的XML文件中,我們把參數的具體值設置進來,如下:
<!DOCTYPE suite SYSTEM "http://beust.com/testng/testng-1.0.dtd" > <suite name="My test suite"> <test name="testing"> <parameter name="number" value="2"/> <classes> <class name="com.fsecure.demo.testng.TestNGTest6_0" /> </classes> </test> </suite> |
那么,如果我們要給的參數比較復雜,我們無法用一個簡單的String或者 原子類型在XML文件”推“給測試方法,那么怎么辦呢?這時候,我們就要用”拉“模型了,我們用一個DataProvider屬性來給被測試的方法上聲明它需要的數據,然后平行的,用@DataProvider注解來標注一個方法,讓這個方法的返回值返回具體的數據,這樣我們2個拼接下就完美了。
比如說:下面代碼聲明我們的測試類需要一個DataProvider:
@Test(dataProvider = "Data-Provider-Function") public void parameterIntTest(TestNGTest6_3_0 clzz) { System.out.println("Parameterized Number is : " + clzz.getMsg()); System.out.println("Parameterized Number is : " + clzz.getNumber()); } |
然后相應的我們用@DataProvider來標注一個方法,這個方法提供了數據:
//This function will provide the patameter data @DataProvider(name = "Data-Provider-Function") public Object[][] parameterIntTestProvider() { TestNGTest6_3_0 obj = new TestNGTest6_3_0(); obj.setMsg("Hello"); obj.setNumber(123); return new Object[][]{ {obj} }; } |
3.測試依賴性:
對于JUnit 來說,所有的測試彼此之間都是獨立的,毫無依賴性。
但是對于 TestNG來說,我們完全可以讓測試彼此之間有依賴性,做法就是dependsOnMethods屬性來標識一個被依賴的測試方法:
@Test public void method1() { System.out.println("This is method 1"); } @Test(dependsOnMethods={"method1"}) public void method2() { System.out.println("This is method 2"); } |
談起項目質量,那是一個讓很多人都頭疼的問題。明明項目規劃的很好,可是等到交付的時候,質量卻是一團糟。其實,很多人把質量和等級混淆了,質量不同于等級。質量是“一系列內在特性滿足要求的程度”,而等級是“對用途相同但技術特性不同的產品或服務的級別分類”。
一個軟件產品可以是高質量低等級的,也可能是低質量高等級的。低質量一定是問題,而低等級不一定是問題。所以,項目經理應與
項目管理團隊負責權衡,以便同時達到要求的質量與等級水平。既然項目質量是一個大問題,接下來我們就一起看看應如何管理項目質量。
項目
質量管理包括執行組織確定質量政策、目標與職責的各過程和活動,從而使項目滿足其預定的需求。它通過適當的政策和程序,采用持續的過程改進活動來實施質量管理體系。項目質量管理包含三大過程,分別是規劃質量、實施質量保證和實施質量控制。
1.規劃質量
規劃質量是識別項目及其產品的質量要求和或標準,并書面描述項目將如何達到這些要求和或標準的過程。規劃質量應與其他項目規劃過程并行開展。例如,為滿足既定的質量標準而對產品提出的變更建議,可能引發成本調整或進度調整或范圍調整。
規劃質量需參考的資料有范圍基準,包括范圍說明書、WBS、WBS詞典等。此外,還包括干系人登記冊、成本績效基準、進度基準、風險登記冊、事業環境因素和組織過程資產。
規劃質量使用的工具與技術有很多,包括成本效益分析、質量成本、控制圖、標桿對照、實驗設計、統計抽樣、流程圖、質量管理方法、其它質量規劃工具等。讀者僅需了解這些內容即可,并根據自己的需要選擇合適的工具。
規劃質量的輸出有質量管理計劃、質量測量指標、質量核對表和過程改進計劃。
2.實施質量保證
實施質量保證是審計質量要求和質量控制測量結果,確保采用合理的質量標準和操作性定義的過程。實施質量保證是一個執行過程,它使用實施質量控制過程所產生的數據,為持續過程改進創造條件。
實施質量保證的依據是項目管理計劃、質量測量指標、
工作績效信息和質量控制測量結果。
實施質量保證的工具與技術有很多,除了質量審計和過程分析之外,它包括規劃質量和實施執行控制所使用的全部工具與技術。
實施質量保證的輸出內容是組織過程資產(更新)、變更請求、項目管理計劃(更新)、和項目文件(更新)。
3.實施質量控制
實施質量控制是檢測并記錄執行質量活動的結果,從而評估績效并建議必要變更的過程。值得注意的是,質量控制的工作貫穿項目的始終。
實施質量控制的依據有項目管理計劃、質量測量指標、質量核對表、工作績效測量結果、批準的變更請求、可交付成果和組織過程資產。
實施質量控制的工具與技術有因果圖、控制圖、流程圖、直方圖、帕累托圖、趨勢圖、散點圖、統計抽樣、檢查和審查已批準的變更請求。其中,前七項被稱作“石川七大基本質量工具”。
實施質量控制的輸出有質量控制測量結果、確認的變更、確認的可交付成果、組織過程資產(更新)、變更請求、項目管理計劃(更新)和項目文件(更新)。
質量是規劃、設計和建造出來的,而不是檢查出來的。預防錯誤的成本通常要比檢查中發現并糾正錯誤的成本少得多。所以,我們在項目開發中應以預防勝于檢查的態度去對待項目。有時候錯誤在所難免,當錯誤發生時,我們應持續改進,提高項目的質量,最終達到客戶滿意。
對于做
web端
自動化測試的人來說,可能接觸selenium比
QTP還要多,但是我們在做基于selenium的二次開發的時候,經常會說到二次開發是 為了易于維護,很多人可能不懂得維護的價值是什么,和到底要維護什么。今天專門寫一篇關于二次開發的
文章,希望能夠幫到有需要做二次開發的人。
二次開發也就是我們常說的封裝selenium,或者做框架。但是一個框架要包含豐富的類和方法。要有一套完整的體系來幫助我們進行封裝。可以說框架的設 計思想就是整個框架的靈魂,如果設計思想很正確也就意味著這個框架成功了一半,剩下的就是我們怎么樣用程序實現這個思想,在開發的過程中我們也許會用到一 些設計模式和引用一些開源框架。這些只是一個開發人員或者程序設計者的基本素質。至于如果把selenium能夠有效的封裝和一些基本思想,我們來詳細的 了解一下。
在這篇文章里面只針對selenium的webdriver來進行討論,我們不再對rc做任何的解釋和說明。我們都知道webdriver的使用過程中, 貫穿始終的就是一個driver, 并且這個driver代表了一個瀏覽器的當前窗口,我們進行操作的過程中只是進行當前窗口的操作,也就是最這個current window進行的一系列的操作,如果我們需要對打開的新的window來進行操作的話,我們需要switchTo,包括操作frame,當然整個流程下 的操作確實讓我們覺得不是很難編寫,但是我們編寫腳本的過程中需要用到的一些輔助功能可能就會很難的編寫,比如最大化瀏覽器,視角
移動到操作的元素等等, 這個過程一次編寫我們可以做到,但是反復的編寫的話肯定是一個讓人很頭疼的過程,所以這個時候我們要去封裝一些常用的方法,我們有了做一個比較完整的框架 的想法,但是我們忽然又意識到了,這樣的話,我們需要把driver封裝起來,因為整個測試的case都是針對的這個driver,并且只有一個 driver,這樣子的話我們不允許創造多個的driver,也就意味著我們要把自己編寫的小工具類和driver聯系起來,并且我們的測試用例case 類也需要調用這個driver,其實很簡單,我們可以用注入的方式來做,把driver當成tools類的一個屬性值,然后注入到我們的case類中,也 可以通過set的方法來進行操作。有了這些基礎,我們可以防止無限的編寫重復的方法,這樣我們有了自己的工具類。如果說這就是框架的話,就會顯得非常的膚 淺,因為我們寫的這些方法根本沒有任何邏輯可言,只是把需要的方法統統的堆到了一起,所以這個時候我們需要想想用到某些方法來進行分一下層次。
Page類和Window類:
PageObject模式我們都知道,就是把資源都放入到page類里面,然后再編寫邏輯類。這樣的話就可以無限的復用這些資源,這只是籠統的講了一下設 計的思想,至于PageObject到底怎么去實現這些設計呢?我們從webDriver的使用開始入手。webdriver是先從定義瀏覽器開始的。 WebDriver driver = new FirefoxDriver(); 這樣我們就定義了一個firefox的瀏覽器,但是自動化的過程不可能只允許我們把定義瀏覽器的操作放在框架代碼里面,那樣的硬編碼方式使我們的case 不存在可移植性了,如果進行兼容性測試的話,維護起來對這些case的修改量是比較大的,這種硬編碼方式是我們不能夠進行大量維護的,所以我們需要把定義 瀏覽器的過程完全放在case類里面,就是在我們寫
測試用例的時候再去編寫到底用什么瀏覽器,防止在編寫框架的時候硬編碼的形式把瀏覽器寫死在了框架里 面。做到多瀏覽器的可維護性,對于我們進行兼容測試也有一定的幫助,這樣的話我們需要對瀏覽器的選擇部分要進行一定的編碼設計,來完成瀏覽器的可選擇性。 在我們定義完了瀏覽器之后,這個時候我們也許覺得就是開始查找元素了,但是在這個driver的基礎上我們應該發現其實這個時候driver代表的整個頁 面的操作。但是在頁面的操作基礎上我們應該意識到還有一個級別的操作,那就是window的操作,就是針對瀏覽器自身的操作。包括一些基本的返回,向前, 最大化,最小化,或者移動到制定元素的位置,調用js等等等,這些方法的級別是出于window級別的,和頁面無關的。所以我們應該把這些所有的方法都封 裝到單獨的一個層次中,我們暫且稱之為window包中,剛才的瀏覽器的選擇的所有方法我們放browser包中。這樣我們設計出了兩個層次。下面的設計 該如何進行呢?我們知道pageObject的思想是把資源都放入到我們定義的page類里面,所以這個時候我們需要思考了,我們如何設計這里的page 類呢?按照pageObject的思想來看,page類應該是我們自己編寫的,那樣我們的框架是不是就可以放棄編寫page類了呢?直接封裝一些通用的方 法?顯然是不對的,對于頁面html源碼操作的過程中,我們煩透了這些元素查找的硬編碼方式,在一個case里面或者兩個case里面反復的調用編寫是讓 人頭疼的一件事情,所以我們可以把所有的單頁面看做一個層次,里面和PageObject的思想一樣,就是放入了通用的方法,但是它存在的意義不只是這樣 簡單,因為我們操作的過程中需要涉及到frame。并且頁面和頁面之間涉及到不同window之間的切換,所以如何協調window和page之間的關系 成為了我們需要注意的難點和重點,我們知道webdriver里面有一個方法叫做getWindowHandlers,這個方法可以獲得所有的句柄,我們 想設計這個page類那么意味著我們需要去完美的配合window類,他們之間唯一的關聯就是這個方法,所以我們可以設計一個概念,叫做頁面集合,在創建 window對象的時候我們就會自動的出現一個頁面集合器,它的功能就是管理所有的在操作過程中打開的頁面。并且能夠指定一個特殊的page,就是當前頁 面。也就是webdriver中的driver對象,因為它一直都是針對當前頁面編程的。那樣我們的window類里面就存在了兩個屬性,一個收集器,一 個當前頁。這樣我們在window的級別上就能夠完全操作page類了,這樣我們在case類設計的時候,只需要通過window類的級別進行編碼就可以 了,完全可以把page類當作一種資源來處理,我們所有需要的東西都是通過page來獲取的。page類的設計中我們一定要編寫通用的方法。比如獲取 title等等等,最主要的一點就是要進行frame的處理操作,我們如何把frame完美的結合在page里面呢?我們在普通的webdriver腳本 編寫過程中可能反反復復的switchTo的方法讓我們很惱火,并且很難讓我們理解這些腳本到底是想表達什么意思呢?所以我們需要進行對page進行層次 上的小分級,就是把page類再分為一個frame的類和一個模塊的類,因為一個大型頁面里面,通用的結構和模塊是很多的。我們單獨的定義一個模塊類,可 以讓這些相同的結構復用在里面的方法。定義frame類主要是處理把定位到frame的操作從case類中脫離出來,我們編寫到page類里面或者 frame類里面,提供一種方式或者方法來進行frame的定位就可以了,簡單實用,并且簡化case類的編寫,畢竟case類并不是由設計者來編寫,盡 量做到最簡化。當然我們可以成這整個層次都是page類,放在page包里面。
Element類:
Element類就是我們常用的driver.findElement()的那種形式,就是元素類,什么是元素類呢?元素就是我們需要定位的那些東西,我 們在操作過程中很難知道一個findElement到底查找的是什么,因為所有的標簽形式都是通過id或者各種各樣的定位方式來實現的。這個時候我們需要 把各種各樣的findElement都統統的放在一起,造成的腳本難以理解讓后續接手的腳本開發人員可能頭疼不已,所以我們可以做一些簡單的加工。當然我 們還要做的一個最大的
工作就是元素查找的封裝。Element和Page類如何關聯起來。我們知道page和webElement的關聯就是一個 driver.findElement的方法。這樣就可以在頁面上查找元素了。所以我們也在element類中通過這種形式來進行他們之間的關聯。我們通 過在element類中添加定位方式的形式來進行元素定位和page的關聯,我們只需要在編寫自己的page類的過程中直接加入element類就可以 了,element類提供了所有的關于element的方法,比如鼠標事件和鍵盤事件,還有更重要的元素定位方法。定位的方法在這里不做任何的推薦,因為 每個人的思路不同,實現的方式也不同,我個人比較偏向的做法是做一個xml來進行資源的管理,把所有需要的資源都放入到xml里面,這樣我們就可以進行元 素的定位了。并且在后期維護中主要維護xml就可以進行對整個腳本進行維護了,不需要我們大量的重新進行源碼的分析和修改了。當然這是設計的優化過程,因 為定位的實現我們還是需要自己來完成的,我們知道元素的定位方式各種各樣,我們怎么來進行管理和定位呢?我們可以通過map的方法作為屬性值來進行元素的 管理,他的各種定位方法存放在map中,我們需要的時候只需要調一下就可以了。通過觀察源碼findElement也是通過map的形式來進行元素定位存 儲的。我們可以借鑒一下源碼的實現方式。當然我們完全封裝findElement也是可以的。說到這里可能我們有一個問題比較難以解決,那就是層級定位。 如果我們只是給element類添加定位方式的話,那么findElement提供的一級一級的定位方式我們就無法應用了,所以在element類中我們 必要還要提供findElement的方法進行層級定位。這只是為了把webdriver的所有方法都盡量應用到而已。其實通過xpath的方式我們就可 以基本上定位大多數的元素。剩下的內容就是我們對element內容的擴充了。我們可以把元素的更加具體的抽象出來,比如我們把 select,table,checkbox等等等的各種html標簽元素顯式的定義出來,在我們定義一個元素的時候我們能夠更加清晰的看到這個元素的含 義,我們知道它是一個按鈕或者table等等等,這些小元素的操作需要我們自己深入理解和開發,這里不做過多的介紹。
其他類:
通過這些層次的分析我們已經出現一個框架的雛形了,然后我們剩下的設計就是基于完善和優化了。在一個自動化過程中case類是非常重要的,我們需要知道 case類運行結束的結果報告和分析,所以case類的運行等等一系列的東西我們都得有統計,這些東西必須要我們提供一些類來實現,不過所幸的是,強大的 junit或者testng完全可以取代我們要去做的工作,他們可以很完美的提供這些功能,我們只需要介入這些開源包就可以了。我們使用QTP的過程中我 們經常會用到參數化,我們自動化的設計都有了,但是沒有參數化的功能怎么辦?我們應該先想象一下數據提供的方式。testng提供了一種參數化的形式,但 是它是需要在xml里面配置或者硬編碼的形式來進行編寫。不過它提供了一種dataprovider的方式來進行參數化,它傳遞參數的形式是 Object[][],我們可能希望使用參數的時候通過excel表格來完成,這些都是可以實現的,poi包提供了解析excel的功能,非常的強大。我 們可以自己編寫解析類來進行參數化的功能編寫,具體實現不再過多去說。調用的方式就簡單多了,硬性的記住幾個注解就可以了。case類的各種運行我們都有 了,還需要一些什么擴展呢?很顯然就是日志的擴展。日志的設計也是很有技巧的。我們需要用日志監控某一些方法的話,如果前期沒有直接加入日志功能,我們可 以通過spring的方式來進行日志切入操作。但是我們在觀察日志的時候我們通常會希望知道到底運行到哪一步了,我們可以想一想,所有的操作都是基于 element或者window的,page的只是一個抽象出來的概念,所以我們只需要把日志加入到每一個element的方法和window的主要方法 里面就可以監控到整個運行的過程,畢竟我們不能夠去親自盯著屏幕一直。這樣沒個方法不外乎就是運行成功和失敗,所以我們可以通過這種方式來進行編碼。日志 的實現我們可以通過通過的log4j或者自己編寫一個小的日志系統。都是可行的方案。
擴展類:
也許我們需要這些系統能夠有良好的可移植性,我們可以自己編寫類加載器,為以后做整個自動化的測試平臺做準備。最主要都是我們做的這些操作可能需要越來簡 單,所以我們可能會因為引入注解的方式來提供編碼效率,所以我們還需要為注解類做一些輔助的工作。當然這些注解的開發需要我們做足足夠的需求研究,并不是 無謂的去開發各種注解。這些注解的應用應該說很廣泛,不再多說注解的好處。并且注解還有一點可應用的地方就是放在數據庫的操作中,在自動化測試中,其實數 據庫的測試也是一個大的難點。在這里我們只討論前端自動化的設計,不過多的討論別的東西。
前面講到的這些類的存在形式其實就是在框架里面的一種層次,我們談論的這些都是基于webdriver的,并且主要基于前端自動化測試的,當然自動化測試 不只包括這些,還包括服務器端,接口自動化,單元自動化等等。我個人的能力水平也是很有限,可能很多地方說到的不是很到位,希望能夠通過這篇文章能夠給那些希望學習自動化,希望編寫小測試框架的童鞋,一點點的啟發。
有幾個學員經常會對線上與線下
測試結果不一樣的問題產生糾結。。。。所以還是統一寫一篇這樣的
文章吧
其實這個問題本身不用糾結,就好比再牛逼的雙胞胎還是有他們不一樣的地方。本身
性能測試就是一個預估風險、排查瓶頸、了解系統現有性能的一個手段。就好比小時候你是個好孩子,但不意味這你長大了也是一個好孩子,也許你會像海波兄那樣的。。。。。so,性能測試只是一種手段,減小風險的方法而已。
再者,本身線上和線下的測試結果就不太具有可比性,原因為:
1、線下與線上機器環境配置的差異
2、線下和線上業務數據的差異,雖然我們線下要最大可能的模擬用戶行為,但你不能拿保證100%的模擬啊,那么多用戶你都能兼顧到??????
3、線下和線上產生壓力時間的差異,線下是模擬高壓力大并發的情況,而線上通常壓力不大,大并發主要集中在某幾個特殊時段。
說道這里,又會有童鞋繼續糾結了,那為毛還做測試啊,都不準確,做個毛毛??????好吧,那我想反問你一句,一輛汽車開的人不同,開車的習慣不同,會對車造成不同程度的影響,既然我們沒法100%測試模擬,那我們干脆就產出汽車后直接賣給你好了,做個什么測試和路測,多tmd費勁。對吧?這時候你不干了,你說那多危險,萬一有大問題呢,不就要了我的命了嗎?呃。。。。這時候你明白了?那換到性能測試中就不明白了?
我們做性能測試的意義其實很簡單:
1、預防、評估風險,如果有大問題可以早點發現,減小風險。這里理解極其簡單,你程序存在內存泄漏的問題,難道線下2g和線上4g這個內存差異就不會有內存泄漏了????????????????這就好比,你不會騎永久牌自行車,難道給你換個小強牌(瞎編的。。。)自行車你就瞬間會騎了?
2、前端性能測試。可以通過前端性能測試保證頁面性能,給用戶帶來較好的用戶體驗。
3、單接口性能調優。主要目的是優化接口性能,排查接口性能問題,及應用內存隱患。
比如,我們會準備幾種業務場景,比如全走DB和全走緩存,分別得到這幾種場景下,應用最佳處理能力情況下,在測試中排查是否存在性能提升的地方,及代碼問題導致的內存泄露等。
4、容量評估。可以根據線上機器比例,線下模擬配比來估算。
下面的這個題你知道輸出結果是什么嗎?試試吧!相信對每一個學
java的同學都是有用的!說不定下次你去
面試就是這個題!當然你是技術大牛可以忽略!
1、 不合理的大表全表掃描
詳見:點擊打開鏈接
v$session_longops視圖記錄了超過6秒的所有
SQL語句
這其中絕大部是全表掃描的語句!
2、 語句共享性不好
常出沒在OLTP,由于app沒有合理使用綁定變量,導致大量重復的語句Parse,浪費大量的shared pool,使CPU利用率居高不下
3、 過量的排序操作
有個原則:能不排序就不排序
特別是multi-pass,與事務設計、缺乏索引、優化器的選擇等均有關系
4、 大量遞歸SQL語句
由sys執行,以大量的空間管理sql語句為甚
常見于大數據處理
作為DBA,大數據處理前,主動進行存儲空間的分配
5、 優化器和統計信息
代碼有時候,在
測試環境能跑,到了生產環境就“萎”了
這是因為,生產環境沒有及時采集統計信息,導致
Oracle優化器不了解最新的數據和應用情況,而錯誤地選擇了非優化的執行路徑
所以,我們需及時采集統計信息,保證基于CBO的優化器能歡快運行
6、 不合理的參數設置
系統參數一定要調,還要合理地調
主要是些內存參數、進程參數等
7、 存儲部署不合理
由于存儲部署不合理導致I/O效率低下
處理方案:ASM、RAID10等
主要是C/S結構比較常見,幾乎絕跡于B/S了
9、 Redo Log 設計不合理
Redo log文件設計太小,頻繁觸發checkpoint事件,導致內存緊張和I/O繁忙
Redo log文件文件組太少,則可能使歸檔無法趕上redo entries產生的速度
在部署一個系統的時候,出現下列問題:
有的客戶端電腦(操作系統Win7或Win8)連接異常緩慢,打開一個業務窗口需要1分鐘-2分鐘,而另外的(
操作系統也是Win7或Win8)連接卻很快,一兩秒就能打開。
在所有的這些客戶端電腦上ping服務器的時候,延遲都在1ms以下,上外網也很快,把慢的電腦直接連到服務器的交換機上也特別慢,因此基本排除網絡方面的原因。
用這些慢的電腦打開
Windows遠程桌面連接Windows2003服務器操作,也是特別的慢。
因此懷疑是操作系統的原因導致的,后來在網上搜索,查到解決方案,如下:
運行命令行命令netsh int tcp set global autotuninglevel=disable(可以寫成批處理文件進行調用)修改系統參數即可解決。
這種情況在Win7、Win8等系統中普遍存在,在本次部署的案例項目上,一半以上的電腦都有此問題,據當初安裝系統的工程師回憶,慢的這些客戶端電腦與正常的電腦確實是用不同的操作系統安裝盤所安裝。
注:global autotuninglevel參數設計的初衷是啟動自動調優.看來,項目上打算使用新技術或者新系統,還需要事先在
測試環境多探索一番.
1.會話標識未更新:登錄頁面加入以下代碼
request.getSession(true).invalidate();//清空session
Cookie cookie = request.getCookies()[0];//獲取cookie
cookie.setMaxAge(0);//讓cookie過期
request.getSession(true).invalidate();//清空session
Cookie cookie = request.getCookies()[0];//獲取cookie
cookie.setMaxAge(0);//讓cookie過期
不是很明白session的機制,高手路過可以指教一下。
2.跨站點請求偽造:
在出錯的url加參數sessionid。
response.getWriter().write( "<script>parent.location.href='dbase/admin/loginJsp.action?sessionId="+sessionId+"'</script>");
response.getWriter().write( "<script>parent.location.href='dbase/admin/loginJsp.action?sessionId="+sessionId+"'</script>");
如果帶參數報ssl錯誤,使用下面的post方式傳值:
response.getWriter().write( "<script language=\"javascript\"> " + "document.write(\"<form action=dbase/admin/loginJsp.action method=post name=formx1 style='display:none'>\");" + "document.write(\"<input type=hidden name=name value='"+sessionId+"'\");" + "document.write(\"</form>\");" + "document.formx1.submit();" + "</script>" ); response.getWriter().write( "<script language=\"javascript\"> " + "document.write(\"<form action=dbase/admin/loginJsp.action method=post name=formx1 style='display:none'>\");" + "document.write(\"<input type=hidden name=name value='"+sessionId+"'\");" + "document.write(\"</form>\");" + "document.formx1.submit();" + "</script>" ); |
3.啟用不安全HTTP方法
修改
web工程中或者服務器web.xml,增加安全配置信息,禁用不必要HTTP方法
<security-constraint> <web-resource-collection> <url-pattern>/*</url-pattern> <http-method>PUT</http-method> <http-method>DELETE</http-method> <http-method>HEAD</http-method> <http-method>OPTIONS</http-method> <http-method>TRACE</http-method> </web-resource-collection> <auth-constraint> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> </login-config> |
修改web工程中或者服務器web.xml,增加安全配置信息,禁用不必要HTTP方法
<security-constraint> <web-resource-collection> <url-pattern>/*</url-pattern> <http-method>PUT</http-method> <http-method>DELETE</http-method> <http-method>HEAD</http-method> <http-method>OPTIONS</http-method> <http-method>TRACE</http-method> </web-resource-collection> <auth-constraint> </auth-constraint> </security-constraint> <login-config> <auth-method>BASIC</auth-method> </login-config> |
4.已解密登錄請求
配置SSL,具體見http://serisboy.iteye.com/admin/blogs/1320231
在web.xml加入如下配置。
<security-constraint> <web-resource-collection > <web-resource-name >SSL</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transportguarantee> </user-data-constraint> </security-constraint> <security-constraint> <web-resource-collection > <web-resource-name >SSL</web-resource-name> <url-pattern>/*</url-pattern> </web-resource-collection> <user-data-constraint> <transport-guarantee>CONFIDENTIAL</transportguarantee> </user-data-constraint> </security-constraint> |
5.高速緩存的ssl頁面
頁面
<meta http-equiv="Pragma" contect="no-cache">
頁面
<meta http-equiv="Pragma" contect="no-cache">
response.setHeader("Pragma", "No-cache");
response.setHeader("Pragma", "No-cache");
6.目錄列表
配置文件目標拒絕訪問。
在conf/web.xml下:
<servlet> <servlet-name> default </servlet-name> <servlet-class> org.apache.catalina.servlets.DefaultServlet </servlet-class> <init-param> <param-name> debug </param-name> <param-value> 0 </param-value> </init-param> <init-param> <param-name> listings </param-name> <param-value> false </param-value> </init-param> <load-on-startup> 1 </load-on-startup> </servlet> <servlet> <servlet-name> default </servlet-name> <servlet-class> org.apache.catalina.servlets.DefaultServlet </servlet-class> <init-param> <param-name> debug </param-name> <param-value> 0 </param-value> </init-param> <init-param> <param-name> listings </param-name> <param-value> false </param-value> </init-param> <load-on-startup> 1 </load-on-startup> </servlet> |
把listings對應的value設置為fasle.
或者把上面的這個servlet加到你的虛擬路徑下的web-inf/web.xml中,把servlet-name改為其它的,再加一下servlet-mapping
<servlet> <servlet-name> default1 </servlet-name> <servlet-class> org.apache.catalina.servlets.DefaultServlet </servlet-class> <init-param> <param-name> debug </param-name> <param-value> 0 </param-value> </init-param> <init-param> <param-name> listings </param-name> <param-value> false </param-value> </init-param> <load-on-startup> 1 </load-on-startup> </servlet> <servlet-mapping> <servlet-name> default1 </servlet-name> <url-pattern> / </url-pattern> <servlet-mapping> <servlet> <servlet-name> default1 </servlet-name> <servlet-class> org.apache.catalina.servlets.DefaultServlet </servlet-class> <init-param> <param-name> debug </param-name> <param-value> 0 </param-value> </init-param> <init-param> <param-name> listings </param-name> <param-value> false </param-value> </init-param> <load-on-startup> 1 </load-on-startup> </servlet> <servlet-mapping> <servlet-name> default1 </servlet-name> <url-pattern> / </url-pattern> <servlet-mapping> |