1、簡介
對編碼完成的功能,進行
測試,是每個程序員最熟悉不過的事了,每完成一部分功能,都需要對實現(xiàn)的功能進行測試,然后才能進行交付。但如何保證自己完成的每個功能都是正確無誤的呢?對,
單元測試!
2.1 pom中增加Junit的jar的依賴
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.8.2</version>
</dependency>
2.2 增加單元測試類
例如,要測試的類為src下的com.runqianapp.userManager.dao.UserDao類中的
publicStringgetUserName(StringuserId)方法
則首先新建一個
test的源碼目錄,原因是測試代碼要與原功能代碼分離,自動構建的時候,只需要把原功能的代碼構建到最新的jar里。然后新建跟被測試類相同的包路徑,如
test下的com.runqianapp.userManager.dao.UserDaoTest
這樣,就為UserDao建好了一個單元測試類,
對于被測試的方法,需要傳不同的參數(shù),來檢驗方法的正確性,這個時候,不必寫多個@Test來測試,而是把需要傳入的參數(shù)放入配置文件,然后讀配置文件。
參數(shù)配置文件的位置是與單元測試類同一目錄下,如
test下com.runqianapp.userManager.dao.testData_UserDao
2.3 JUnit的常用注解介紹
2.3.1 @Test
需要運行的單元測試方法,可以有多個
2.3.2 @Before
在每一個@Test方法運行之前都會被運行,可以用來初始化方法
2.3.3 @Before
在每一個@Test方法運行之后都會被運行,可以用來方法的釋放資源
2.3.4 @BeforeClass
針對整個單元測試類,只會被運行一次,在所有方法運行之前被運行,可以用來初始化環(huán)境
必須聲明成staticvoid
2.3.5 @AfterClass
針對整個單元測試類,只會被運行一次,在所有方法運行之后被運行,可以用來釋放資源
必須聲明成staticvoid
2.4 斷言
對于需要測試的方法,用斷言來判斷其它執(zhí)行結果是否正確。
assertEquals([Stringmessage],expected,actual)
message是個可選的消息,將會在發(fā)生錯誤時報告這個消息。
expected是期望值,通常都是用戶指定的內容。
actual是被測試的代碼返回的實際值。
如:
booleansuccess=update();
assertEquals(“更新是否成功”,true,success)
這樣,當運行update()的返回值不為true的時候,這個單元測試就會失敗。
但有些時候,我們測試的方法并沒有返回值,方法只是執(zhí)行一個動作,那么這個時候,我們就不能用判斷的返回值來判斷方法是否執(zhí)行成功。
如新建文件的方法,publicvoidcreateFile(StringfilePath),這個時候,方法并沒有返回值,我們在判斷方法是否執(zhí)行成功的時候,可以寫一個輔助方法,來檢查一下指定的文件是否新建成功了,以此來驗證createFile()的準備性。如:
createFile(“d:/a.txt”); booleanexist=fileExist(“d:/a.txt”); assertEquals(“新建文件”,true,exist); |
3、JMockit
當我們在編寫單元測試的時候,常常會出現(xiàn)一些如調用的方法需要其它對象提供,而這個類現(xiàn)在又不具備,如很難創(chuàng)建、沒有環(huán)境、沒有開發(fā)完等情況,這個時候,我們就需要用JMockit模擬出一個類,來滿足我們的需求,來完成我們核心功能的測試。
3.1 pom中增加JMockit的依賴
<dependency> <groupId>jmockit</groupId> <artifactId>jmockit</artifactId> <version>1.0</version> <scope>test</scope> </dependency> |
3.2 模擬對象
如需要測試的一個方法的一個值是配置在misInitConfig.xml中的,而misInitConfig.xml中的值需要用ReadConfInfo.getPropery()來獲取,而ReadConfInfo是在應用啟動時初始化的,這個時候并沒有web環(huán)境,也不能初始化,而我們也不需要自己手動初始化ReadConfInfo,因為我們測試的重點不是ReadConfInfo,而是我們現(xiàn)有的功能。這個時候,我們就可以用JMockit模塊出ReadConfInfo,來完成此功能的測試。
/** *模擬ReadConfInfo */ publicstaticvoidmockReadConfInfo(){ newMockUp<ReadConfInfo>(){ @Mock publicStringgetPropery(StringpropertyName){ if("conf_loglevel".equals(propertyName)){ return"debug"; }elseif("conf_logfolder".equals(propertyName)){ return"log"; }else{ return""; } } } } |
在單元測試的@BeforCalss中初始化,
/** *初始化環(huán)境 */ @BeforeClass publicstaticvoidinit()throwsException{ //模擬ReadConfInfo mockReadConfInfo(); } |
這樣,我們就可以在程序中使用ReadConfInfo.getPropery()來完成我們的功能了。
1.sudo介紹
sudo是linux下常用的允許普通用戶使用超級用戶權限的工具,允許系統(tǒng)管理員讓普通用戶執(zhí)行一些或者全部的root命令,如halt,reboot,su等等。這樣不僅減少了root用戶的登陸和管理時間,同樣也提高了安全性。Sudo不是對
shell的一個代替,它是面向每個命令的。它的特性主要有這樣幾點:
sudo能夠限制用戶只在某臺主機上運行某些命令。
sudo提供了豐富的日志,詳細地記錄了每個用戶干了什么。它能夠將日志傳到中心主機或者日志服務器。
sudo使用時間戳文件--日志 來執(zhí)行類似的“檢票”系統(tǒng)。當用戶調用sudo并且輸入它的密碼時,用戶獲得了一張存活期為5分鐘的票(這個值可以在編譯的時候改變)。
sudo的配置文件是sudoers文件,它允許系統(tǒng)管理員集中的管理用戶的使用權限和使用的主機。它所存放的位置默認是在/etc/sudoers,屬性必須為0411。
2.下面詳細解釋配置實例:
[root@ocm1 ~]# rpm -qa|grep sudo --查詢是否已經安裝SUDO,一般是安裝了的。
sudo-1.6.9p17-5.el5
[root@ocm1 ~]# visudo ---root使用visudo會默認打開/etc/sudoers文件,
root ALL=(ALL) ALL ---系統(tǒng)默認只有這一行
oracle ALL=(ALL) ALL ----為ORACLE用戶增加這一行,
配置完成后,重新打開會話,使用 sudo vi /etc/hosts,,往里面增加并保存測試SUDO是否可用。
3.配置中可能碰到的問題:
1、主機名配置錯誤導致sudo不能執(zhí)行和日志紀錄。
錯誤提示:Sorry, user
test is not allowed to execute '/bin/cat /etc/sudoers' as root on
localhost.localdomain.
解決:將其中的localhost改為真實主機名字或IP即可
test localhost=/sbin/cat /etc/sudoers
Defaults@localhost log_host /var/log/sudo.log
2、命令別名列表中命令錯誤導致sudo不能執(zhí)行
錯誤提示:[jackyu@localhost jackyu]$ sudo cat /etc/sudoers
Sorry, user jackyu is not allowed to execute '/bin/cat /etc/sudoers' as root on
localhost.localdomain.
解決:由于在Cmnd alias里定義的時候命令書寫有誤(Cmnd_Alias CAT = /bin/cat -n /etc/sudoers).
執(zhí)行:sudo cat -n /etc/sudoers
1,進入MySQL目錄下的bin文件夾:cd MySQL中到bin文件夾的目錄
如我輸入的命令行:cd C:\Program Files\MySQL\MySQL Server 4.1\bin
(或者直接將windows的環(huán)境變量path中添加該目錄)
2,導出數(shù)據(jù)庫:mysqldump -u 用戶名 -p 數(shù)據(jù)庫名 > 導出的文件名
如我輸入的命令行:mysqldump -u root -p news > news.sql (輸入后會讓你輸入進入MySQL的密碼)
(如果導出單張表的話在數(shù)據(jù)庫名后面輸入表名即可)
3、會看到文件news.sql自動生成到bin文件下
命令行導入數(shù)據(jù)庫:
1,將要導入的.sql文件移至bin文件下,這樣的路徑比較方便
2,同上面導出的第1步
3,進入MySQL:mysql -u 用戶名 -p
如我輸入的命令行:mysql -u root -p (輸入同樣后會讓你輸入MySQL的密碼)
4,在MySQL-Front中新建你要建的數(shù)據(jù)庫,這時是空數(shù)據(jù)庫,如新建一個名為news的目標數(shù)據(jù)庫
5,輸入:mysql>use 目標數(shù)據(jù)庫名
如我輸入的命令行:mysql>use news;
6,導入文件:mysql>source 導入的文件名;
如我輸入的命令行:mysql>source news.sql;
MySQL備份和還原,都是利用mysqldump、mysql和source命令來完成的。
1.Win32下MySQL的備份與還原
1.1 備份
開始菜單 | 運行 | cmd |利用“cd \Program Files\MySQL\MySQL Server 5.0\bin”命令進入bin文件夾 | 利用“mysqldump -u 用戶名 -p databasename >exportfilename”導出數(shù)據(jù)庫到文件,如mysqldump -u root -p voice>voice.sql,然后輸入密碼即可開始導出。
1.2 還原
進入MySQL Command Line Client,輸入密碼,進入到“mysql>”,輸入命令"show databases;",回車,看看有些什么數(shù)據(jù)庫;建立你要還原的數(shù)據(jù)庫,輸入"create database voice;",回車;切換到剛建立的數(shù)據(jù)庫,輸入"use voice;",回車;導入數(shù)據(jù),輸入"source voice.sql;",回車,開始導入,再次出現(xiàn)"mysql>"并且沒有提示錯誤即還原成功。
2.Linux下MySQL的備份與還原
2.1 備份
[root@localhost ~]# cd /var/lib/mysql (進入到MySQL庫目錄,根據(jù)自己的MySQL的安裝情況調整目錄)
[root@localhost mysql]# mysqldump -u root -p voice>voice.sql,輸入密碼即可。
2.2 還原
法一:
[root@localhost ~]# mysql -u root -p 回車,輸入密碼,進入MySQL的控制臺"mysql>",同1.2還原。
法二:
[root@localhost ~]# cd /var/lib/mysql (進入到MySQL庫目錄,根據(jù)自己的MySQL的安裝情況調整目錄)
[root@localhost mysql]# mysql -u root -p voice<voice.sql,輸入密碼即可。
一、泛型概述
jdk1.5版本以后出現(xiàn)的新特性,用于解決安全問題,是一個安全機制。
好處:
1,將運行時期的問題ClassCastException轉到了編譯時期。
2,避免了強制轉換的麻煩。
什么時候用:
當操作的引用數(shù)據(jù)類型不確定的時候。就使用泛型(<>)。將要操作的引用數(shù)據(jù)類型傳入即可,其實<>就是一個用于接收具體引用數(shù)據(jù)類型的參數(shù)范圍。
在程序中,只要用到了帶有<>的類或者接口,就要明確傳入的具體引用數(shù)據(jù)類型。
泛型技術是給編譯器使用的技術,用于編譯時期。確保了類型的安全。
運行時,會將泛型去掉,生成的class文件中是不帶泛型的,這個稱為泛型的擦除。
泛型的通配符:未知類型。
二、泛型的使用
publicclass GenericDefineDemo4 { publicstaticvoid main(String[] args) { ArrayList<String> arr=new ArrayList<String>(); arr.add("java01"); arr.add("java02"); arr.add("java03"); arr.add("java04"); Iterator<String> it=arr.iterator(); while(it.hasNext()) { System.out.println(it.next()); } } } |
三、泛型類
1、格式
class ClassName<datatype>
{
}
2、優(yōu)點
便于對任何未知的的數(shù)據(jù)類型進行操作,特別適合用作工具類,避免了強制類型轉換的麻煩。
3.示例
import java.util.*; publicclass GenericClass { publicstaticvoid main(String[] args) { Tools<String> tools=new Tools<String>("hello"); System.out.println(tools.getObj()); } } class Tools<T> { private T obj; public Tools(T t) { this.obj=t; } public T getObj() { returnobj; } publicvoid setObj(T obj) { this.obj = obj; } } |
四、泛型方法
當只需要某一個方法可以接收不同類型的參數(shù),而不是整個類的時候,就可以用到泛型方法,它將大大提高程序代碼的擴展性。
示例代碼如下:
import java.util.*; publicclass GenericMethod { publicstaticvoid main(String[] args) { String s="hello!Nice to meet you!"; Integer inter=3; Show show=new Show(); show.print(s); show.print(inter); } } class Show { public <Q> void print(Q q) { System.out.println(q); } } |
五、靜態(tài)泛型方法
1、格式
public static <T> methodName(T param)
{
}
2、特點
由于靜態(tài)方法不可以訪問類中的泛型,如果靜態(tài)方法操作的類型不確定,這可以把泛型定義在靜態(tài)方法上。
3、示例
publicstatic <Q> void print(Q q)
{
System.out.println(q);
}
六、泛型接口
1、格式
interface Inter<T>
{
void show(T t);
}
2、特點
當接口要操作的數(shù)據(jù)類型不確定的時候,就可以使用泛型接口。
注意:
當實現(xiàn)接口的類已經確定了接口具體的類型時,創(chuàng)建對象時就不用再指明類型了,如:
class InterImpl implements Inter<String>
{
}
如果實現(xiàn)類仍不知道具體的類型,就要創(chuàng)建對象時指明,如:
class InterImpl<T> implements Inter<T>
{
}
InterImpl<String> im=new InterImpl<String>();
3、示例
import java.util.*; public class Test { public static void main(String[] args) { InterImpl<String> im=new InterImpl<String>(); im.print("hello"); } } interface Inter<T> { void print(T t); } class InterImpl<T> implements Inter<T> { public void print(T s) { System.out.println(s); } } |
七、泛型extends限定
extends E:接收E類型或者E的子類型對象。上限
一般存儲對象的時候用。比如添加元素 addAll.
八、泛型super限定
super E:接收E類型或者E的父類型對象。下限。
一般取出對象的時候用。比如比較器。
android和
java程序的
測試有點小小的區(qū)別。在java中我們可以在每個類中都可以擁有一個靜態(tài)的main方法,我們可以用來測試該類里面的代碼的正確性。但是在android中這點確實行不通。在今天,用一個junit來進行測試。
新建一個測試例子的步驟如下:
第一,要在清單文件中導入相關的類:
<uses-library android:h=name="android.test.runner"/>這句話在<application>標簽中進行添加,指定了進行測試程序需要理考的共享庫。
第二,在清單中指定測試工具。用<instrumentation android:targetPackage="xxxxxxxx"(指定包名)
android:name="android.test.InstrumentationTestRunner"
/>來指定測試的工具類。
第三,假如你要進行對如下的類來測試:
class A { public int add(int c,int d) { System.out.println("a+b="+(a+b)); } } |
這是一個非常簡單的類,如果要測試這個類就要再定義一個測試類,該測試類可以繼承AndroidTestCase這個類。
class TestClass extends AndroidTestCase { public void testMethod() { //新建一個類的實例 A a=new A(); //調用要測試的方法 a.add(2,3); } } |
這樣就可以打印出結果來達到測試的目的。
定義
單元測試是一段自動化的代碼,用來調用被測試的方法或類,而后去驗證基于該方法或類的一些假設,
白盒測試的一種。
特性
自動化
可復用
易于實現(xiàn)
快速運行
一個優(yōu)秀的單元測試,應該是全自動的、可信賴的、可讀性強、可維護的。
目的
幫助程序員較早的發(fā)現(xiàn)代碼缺陷,提升產品質量,節(jié)省人力成本。
實踐
這里我們使用VS2010自帶的Unit
Test進行單元測試。
場景如下,我們現(xiàn)在有一個功能模塊能夠實現(xiàn)計算器的功能,代碼如下:
我們需要對它進行單元測試,如圖:
添加測試項目后,添加如下測試類型:
對計算器中的除法先進行測試:
右鍵Run Test(也可在左側Run),運行后,結果為綠色,表示通過:
但如果我們對測試代碼稍做調整,將除法的被除數(shù)變?yōu)?,看看會發(fā)生什么情況:
單元測試
結果顯示Failed,發(fā)生錯誤,表示我們的代碼存在問題,未考慮除零錯誤,對代碼進行調整:
重新運行后通過,到這里,一個簡單的測試就完成了。
性能測試常用術語
性能測試的應用領域
性能測試工具原理
2.LoardRunner基本操作及應用
LoardRunner原理分析
LoardRunner腳本開發(fā)流程
VuGen(基本設置、檢查點、參數(shù)化、關聯(lián)、調試、事物)
Controller(場景設計、場景監(jiān)視<Windows Resources、IIS服務器、Unix/Linux Resource、Weblogic Server、
數(shù)據(jù)庫服務器>、集合點、IP欺騙技術、負載均衡、場景執(zhí)行)
Analysis(摘要報告、常見圖分析、結果分析實踐<分析圖合并、分析圖關聯(lián)、頁面細分、鉆取技術、外部數(shù)據(jù)導入、HttpWatch分析>)
3.Linux監(jiān)測、分析、調優(yōu)
CPU監(jiān)控
內存監(jiān)控
磁盤監(jiān)控
網絡監(jiān)控
4.Windows監(jiān)測、分析、調優(yōu)
LoardRunner直接監(jiān)控、
Windows性能工具監(jiān)控、Windows計數(shù)器
5. 數(shù)據(jù)庫監(jiān)測、分析、調優(yōu)
6. 常用的服務器監(jiān)測、分析、調優(yōu)
Apache監(jiān)控
Apache調優(yōu)
Tomcat監(jiān)控
7. 前端性能調優(yōu)
前端性能分析概要
Firebug工具
HttpWatch
Chrome自帶的開發(fā)工具
DynaTrace Ajax Edition工具
8.網絡分析
9.代碼分析與調優(yōu)
10.項目實戰(zhàn)
11.推薦書籍
<<軟件性能測試過程詳解與案例剖析>>
<<性能測試進階指南>>
<<深入性能測試LoardRunner>>
Linux系統(tǒng)環(huán)境變量配置文件: /etc/profile : 在登錄時,操作系統(tǒng)定制用戶環(huán)境時使用的第一個文件 ,此文件為系統(tǒng)的每個用戶設置環(huán)境信息,當用戶第一次登錄時,該文件被執(zhí)行。
/etc /environment : 在登錄時
操作系統(tǒng)使用的第二個文件, 系統(tǒng)在讀取你自己的profile前,設置環(huán)境文件的環(huán)境變量。
~/.profile : 在登錄時用到的第三個文件 是.profile文件,每個用戶都可使用該文件輸入專用于自己使用的
shell信息,當用戶登錄時,該文件僅僅執(zhí)行一次!默認情況下,他設置一些環(huán)境變量,執(zhí)行用戶的.bashrc文件。
/etc/bashrc : 為每一個運行bash shell的用戶執(zhí)行此文件.當bash shell被打開時,該文件被讀取.
~/.bashrc : 該文件包含專用于你的bash shell的bash信息,當?shù)卿洉r以及每次打開新的shell時,該該文件被讀取。
PASH環(huán)境變量的設置方法:
方法一:用戶主目錄下的.profile或.bashrc文件(推薦)
登錄到你的用戶(非root),在終端輸入:
$ sudo gedit ~/.profile(or .bashrc)
可以在此文件末尾加入PATH的設置如下:
export PATH=”$PATH:your path1:your path2 ...”
保存文件,注銷再登錄,變量生效。
該方式添加的變量只對當前用戶有效。
方法二:系統(tǒng)目錄下的profile文件(謹慎)
在系統(tǒng)的etc目錄下,有一個profile文件,編輯該文件:
$ sudo gedit /etc/profile
在最后加入PATH的設置如下:
export PATH=”$PATH:your path1:your path2 ...”
該文件編輯保存后,重啟系統(tǒng),變量生效。
該方式添加的變量對所有的用戶都有效。
方法三:系統(tǒng)目錄下的 environment 文件(謹慎)
在系統(tǒng)的etc目錄下,有一個environment文件,編輯該文件:
$ sudo gedit /etc/environment
找到以下的 PATH 變量:
PATH="<......>"
修改該 PATH 變量,在其中加入自己的path即可,例如:
PATH="<......>:your path1:your path2 …"
各個path之間用冒號分割。該文件也是重啟生效,影響所有用戶。
注意這里不是添加export PATH=… 。
方法四:直接在終端下輸入
$ sudo export PATH="$PATH:your path1:your path2 …"
這種方式變量立即生效,但用戶注銷或系統(tǒng)重啟后設置變成無效,適合臨時變量的設置。
注 意:方法二和三的修改需要謹慎,尤其是通過root用戶修改,如果修改錯誤,將可能導致一些嚴重的系統(tǒng)錯誤。因此筆者推薦使用第一種方法。另外嵌入式 Linux的開發(fā)最好不要在root下進行(除非你對Linux已經非常熟悉了!!),以免因為操作不當導致系統(tǒng)嚴重錯誤。
下面是一個對environment文件錯誤修改導致的問題以及解決方法示例:
問題:因為不小心在 etc/environment里設在環(huán)境變量導致無法登錄
提示:不要在 etc/environment里設置 export PATH這樣會導致重啟后登錄不了系統(tǒng)
解決方法:
在登錄界面 alt +ctrl+f1進入命令模式,如果不是root用戶需要鍵入(root用戶就不許這么羅嗦,gedit編輯會不可顯示)
/usr/bin/sudo /usr/bin/vi /etc/environment
光標移到export PATH** 行,連續(xù)按 d兩次刪除該行;
輸入:wq保存退出;
然后鍵入/sbin/reboot重啟系統(tǒng)(可能會提示need to boot,此時直接power off)
selenium獲取input時候,發(fā)現(xiàn)type=”hidden” 的input無法修改value,經牛人指點,可以使用js修改
首先html源文件如下,設置為text 、hidden、submit
View Code
在瀏覽器加載之后如下:
這時候email 不能對外顯示
使用selenium,代碼如下
1 import org.openqa.selenium.Alert; 2 import org.openqa.selenium.JavascriptExecutor; 3 import org.openqa.selenium.By; 4 import org.openqa.selenium.WebDriver; 5 import org.openqa.selenium.WebElement; 6 import org.openqa.selenium.chrome.ChromeDriver; 7 //import org.openqa.selenium.ie.InternetExplorerDriver; 8 //import org.openqa.selenium.remote.DesiredCapabilities; 9 10 public class selenium { 11 12 /** 13 * @param args 14 * @throws InterruptedException 15 */ 16 public static void main(String[] args) throws InterruptedException { 17 // TODO Auto-generated method stub 18 19 String URL="E:\\2.html"; 20 //set web driver property 21 System.setProperty("webdriver.chrome.driver", "E:\\chromedriver.exe"); 22 //create a WebDriver instance 23 WebDriver driver = new ChromeDriver() ; 24 driver.manage().window().maximize(); 25 26 //load the URL 27 driver.get(URL); 28 //print current title 29 System.out.println(driver.getTitle()); 30 //run JS to modify hidden element 31 ((JavascriptExecutor)driver).executeScript("document.getElementById(\"em\").type ='text';"); 32 Thread.sleep(3000); 33 //run JS and add a alert 34 ((JavascriptExecutor)driver).executeScript("alert(\"hello,this is a alert!\");value=\"Alert\""); 35 36 //wait for 3 seconds 37 Thread.sleep(3000); 38 39 40 //create a alert instance 41 Alert alert1=driver.switchTo().alert(); 42 //print alert text 43 System.out.println(alert1.getText()); 44 //click accept button 45 alert1.accept(); 46 47 //create elements 48 WebElement we=driver.findElement(By.id("fn")); 49 WebElement su=driver.findElement(By.id("su")); 50 WebElement em=driver.findElement(By.id("em")); 51 // input something 52 we.sendKeys("username test"); 53 Thread.sleep(3000); 54 //print email tagname 55 System.out.print("Email isDislayed="+em.isDisplayed()+"\n"); 56 Thread.sleep(3000); 57 //click submit button 58 su.click(); 59 Thread.sleep(3000); 60 61 Alert alert=driver.switchTo().alert(); 62 System.out.print( alert.getText()); 63 alert.accept(); 64 65 Thread.sleep(3000); 66 67 //close web browser 68 driver.quit(); 69 70 } 71 72 } |
可以通過js修改input的type value,執(zhí)行js只需要export
import org.openqa.selenium.JavascriptExecutor;
運行結果如下:
Starting ChromeDriver (v2.9.248315) on port 30175
this is a test
hello,this is a alert!
Email isDislayed=true
I am an alert box!!
上一篇里我們討論了
測試的必需性,如果大家目前還在公司里做著測試的
工作,那就說明還是落在必需的范圍里面,或者至少一段時間是吧。那接下來我們看下既然需要做測試,需要做哪些事情。
基于我自己的一些理解和觀察,我試圖把測試工作的層次分成三個階段,越到后面涵蓋的范圍越廣。這里討論的一些做法可能更偏向于
互聯(lián)網方面的測試,特別是第三個階段。
首先我想先從一個例子開始,一個現(xiàn)實
生活中的例子。
對于一個城市,假設我們的工作目標是提升環(huán)境的質量,減少垃圾。那么我們可以做什么?
首先,我們可以請很多環(huán)衛(wèi)工人,出去打掃各個街道,這個馬上就有了效果,環(huán)境變得更干凈了。但是還不夠好的地方是明天還是有很多東西需要打掃,治標不治本,只要一停下來立馬回到之前的狀況。
接下來,我們往前面想一想,為什么有那么多垃圾呢?其中一個方面是很多人亂扔垃圾。所以更進步一點的方案是,對于亂扔垃圾的人有些約束或者懲罰,比如抓到了曝光或者罰錢,這樣扔垃圾的人會變少。
再然后,我們發(fā)現(xiàn)即使做到了上面,還是有不少垃圾,而且上面強制的方案也帶來不少的反感。我們需要更深層次的思考,為什么會有那么多垃圾?是因為垃圾桶太少?設計得不合理?如果是這樣,就需要從其他公共設施方面做一些改進了。
對于我們的測試工作,也是有類似的思路,只不過細節(jié)上要考慮更多。
第一個階段:發(fā)現(xiàn)和解決bug的階段
這個階段的思路基本上盡可能發(fā)現(xiàn)更多的bug,見一個滅一個,來兩個滅一雙。
發(fā)現(xiàn)bug,解決后驗證bug,沒有任何根源性的推動,或者推動的效果不好。
這個階段,測試工作主要集中在發(fā)現(xiàn)bug,要做好這個,需要多個方面的努力,比如下面這些:
- 更高效的發(fā)現(xiàn)bug,考驗測試設計的能力。
這方面有非常多的方法和技巧,以及經驗,這里不細說。
- 發(fā)現(xiàn)bug之后如何清晰的描述,定級,以及跟進和驗證。
這個看似簡單,但是你會發(fā)現(xiàn)很多測試工作做了幾年的人這樣的基本功還是不夠扎實。也可能沒有受到過很好的訓練或者一直沒有人指導。
- 對業(yè)務和架構的理解能力。
沒有這方面的能力,很難發(fā)現(xiàn)一些深層次的bug。而這樣的能力對于快速
學習和一些技術基礎也有不低的要求。
- 發(fā)現(xiàn)bug之后如果舉一反三的盡早發(fā)現(xiàn)更多類似的bug。
大家看到的很多經典的測試書籍講的基本都是這個階段做的事情,比如Software Testing,How We Test Software at Microsoft,以及探索性測試相關的書籍,都是專注在如何更高效的發(fā)現(xiàn)缺陷。
上面這些東西都是一個業(yè)務測試人員的基本功。看似簡單,但是做好并不是一件容易的事情。也許這些事情一點都不cool,不sexy,甚至去做職級評審的時候不占優(yōu)勢,但是對于系統(tǒng)質量的提升,是切切實實帶來很大幫助的,其工作的價值應該得到認可。但是如果一直停留在這個階段,就陷入到上面例子中說的掃馬路的階段,因為如果沒有其他方面的改變,每次都有那么多的bug。
不過很多時候,我們的測試停留在這個階段也是因為現(xiàn)狀,考慮下這樣的情況:
- 開發(fā)基本不自測,甚至沒有自測的環(huán)境,特別是涉及多個系統(tǒng)的對接。
- 提測后很多基本的功能都不能正常使用
-
項目管理比較混亂,但是最終的發(fā)布日期又被老大們定死,所以測試時間常常被壓縮
而且,而且沒有對于開發(fā)人員的質量方面的考核,那么很長一段時間,我們的測試將處于這個初級階段。
我相信目前還有不少的團隊是處于這樣疲于應對的情況下,不只是小公司,可能一些大公司的部分項目也是如此。隨著整個研發(fā)體系的發(fā)展和深入,我們應該有更高的追求。
第二個階段:質量的管理
在第一個階段中,可能有一些人會停下來想:我們一直這樣下去也不是個辦法?有沒有更好的做法呢?
最直接會想到的就是,怎么讓別人少丟垃圾,讓本身的bug就更少一些。如果我們做的工作只是發(fā)現(xiàn)bug解決bug,那么就是一個消耗戰(zhàn)。不能形成一個良性的循環(huán),就不能持續(xù)的優(yōu)化,工作的長期累積價值就體現(xiàn)不出來。
這個階段核心的思路是對缺陷做分析和考核,并做研發(fā)流程中主要問題的梳理和改善。
常常做的事情可以從下面幾個方面來看:
1. 做質量數(shù)據(jù)的統(tǒng)計和分析
收集的數(shù)據(jù)很多,常見的有:
- 外網的bug情況,包括事故,及影響的程度
- 測試階段的bug數(shù)量,分布(按系統(tǒng),團隊,開發(fā)個人),嚴重程度,bug的類別等維度
- bug的橫向跨團隊和系統(tǒng)的對比,縱向的和歷史情況對比
- 版本發(fā)布的情況,代碼變更行數(shù)的情況
從這些數(shù)據(jù)的收集中就能發(fā)現(xiàn)很多問題,比如問題集中在哪里,哪些模塊,哪些人,哪些類別等等,以及有沒有改善。
2. 問題的追溯和對于開發(fā)的考核
這個方面也許有一些爭議,但是我還是覺得這個是一個很重要的方法。光靠觀念和自覺是不夠的,必需要有一定的反饋機制,就好比交規(guī)一定是配合著扣分和罰款等手段,否則記錄闖紅燈有什么意義呢?而且現(xiàn)實的來說,這些方法起到約束的作用,也是一種心理暗示,要做自己做的東西負責,也便于養(yǎng)成好的習慣。
通常的考核指標涉及這些方面:
- 編譯失敗次數(shù)的考核
- 外網事故和bug的數(shù)量
- 測試階段的bug,特別是基礎功能bug和嚴重bug
粗略的列了這么多,其實可以有很多,比如配置文件改錯的情況,漏提測文件的次數(shù)等等。
這里也許有很多的討論,但是讓我們看看一個實際的例子。下圖是某個系統(tǒng)的編譯失敗的情況,在11月份的時候提出要統(tǒng)計并公開(并無懲罰條款)編譯失敗的情況,包含到開發(fā)的團隊和個人等明顯,12月份開始出現(xiàn)了明顯的下降并穩(wěn)定了。這個圖隱藏了一些細節(jié),如果剔除其他因素只看開發(fā)代碼原因的編譯失敗則更明顯,特別是后面有懲罰機制之后,進一步下降。
編譯失敗大幅的下降一方面是節(jié)省了大家的時間,另一方面其實也是提高了版本質量,想想如果有很多的編譯失敗,而且是到提交測試的階段,這樣的代碼質量能好嗎?是可能做過自測嗎? 有了這樣的機制,至少會更仔細一些。
對于bug方面其實也是一樣,如果開發(fā)在乎(或者被迫在乎)外網bug或者被測試發(fā)現(xiàn)的bug數(shù)量,他寫代碼的時候一定會更仔細,也會做些簡單的自測,讓提測的質量更高,提高了整個研發(fā)系統(tǒng)的效率,同時也是提升了質量,因為quality must be built in。
我個人的經驗,作為測試人員幾乎同時面對過兩個開發(fā)團隊,一個有上述的考核,一個沒有。表現(xiàn)出來的版本質量和對質量的關注完全不一樣,而且前者也并沒有出現(xiàn)開發(fā)和測試的對立,以及測試不敢提bug等負面的情況。
3. 對于測試的考核
除了對于開發(fā)的考核,同樣也有對于測試的考核,這樣也更加的公平。
測試的考核通常考慮下面的指標:
- 漏測:絕對數(shù)量或者漏測率
- 版本的工作量和測試效率
- 發(fā)布延期的情況
如果測試有這樣的壓力,也需要不斷努力去發(fā)現(xiàn)更多的bug。
說起考核,總有人覺得這不符合智力勞動的情況,或者互聯(lián)網的作風,其實不太理解為什么會這么覺得,放眼望去,有什么工作不被考核呢,sales要背quota,為什么軟件開發(fā)和測試不能對自己的工作的質量負責呢?當然,具體的指標如何去定才更合理那是另一個要去考慮的。
換個角度來看,適當?shù)膲毫Γú粦搶е陆箲]和扭曲的做法),其實是讓一個人表現(xiàn)最好的狀態(tài)。
4. 推動開發(fā)的自測
這個問題一向是個老大難問題。愿意自測的開發(fā)團隊你不用太多的推動,不愿意做的推動也很難,或者你無法判斷他有沒有做自測。而且這方面,通常取決于開發(fā)負責人的觀念和態(tài)度。
如果是介于之間的,我們可以做一些事情,比如:
- 統(tǒng)計測試階段的bug中,屬于開發(fā)可自測發(fā)現(xiàn)的比例。通過這個可以看有多少bug是不應該到測試階段的,以及橫行縱向的對比。當然這個標準要自己拿捏。
- 給出一個自測的checklist。開發(fā)在提交前要完成這個list并正式的給出報告。這個方式我們曾經在一個項目中用過,效果不錯,基本功能都通過這個保證了,前提是開發(fā)負責人認可。
- 有一套自動化驗收的用例,可以掛接到自動部署之后或者daily build。前提是我們的自動化要足夠的問題,效果才會好。
這個階段除了業(yè)務測試的努力,也體現(xiàn)出了QA的價值。這里的QA是指質量管理,有的地方叫SQA,專注在質量度量和研發(fā)流程的管理上。
到這個階段,發(fā)現(xiàn)事情順了很多,質量也有更大程度的提升,并有改善額趨勢。
第三個階段:推動全面的質量提升
到上面第二個階段,我們發(fā)現(xiàn)質量有了一定的提升,但是還是有不少的問題,而且有些問題需要我們把思路和眼界拓寬來看。這里討論的一些東西可能更適合互聯(lián)網的產品。
這里列一些我們可以去做的事情,受限于個人的經驗,可能還很片面。
1. 研發(fā)流程的梳理
其實在階段2的時候也可能有些團隊已經開始做這樣的事情,因為在分析質量和效率問題的時候,我們發(fā)現(xiàn)很多問題不單純是代碼的問題,可能還涉及研發(fā)流程的很多方面,比如:
- 需求不清楚
- 跨團隊的配合問題
- 代碼版本管理
- 技術方面的評審和大家的理解
所以整個研發(fā)流程的規(guī)范和梳理,以及配合對應的需求和版本管理的系統(tǒng)也是非常的必要,實際中發(fā)現(xiàn)效果也是比較的明顯。而且還有一點體會,在接手一個很混亂的狀況時,這樣角度的數(shù)量和調整比技術方案的引入更重要和切中要點,能從40分到60分,技術是往80分走的過程效果更明顯。
2. 提交測試前后做的一些事情
- 代碼的靜態(tài)掃描
這個方法很多的團隊都在做,但是實際的效果似乎差別很多,而且ROI也很難說,不過從方法本身來說還是值得去做的,對測試人員也提出來更高的要求。
- code review
這個開發(fā)應該要做,特別是開發(fā)間的交叉review,非常的有幫助。不過這個也和自測一樣,取決于開發(fā)負責人的態(tài)度。另外,測試也應該去做,特別是對于diff 代碼的review,我們檢查做了大概兩個月的時間,發(fā)現(xiàn)還是非常的有收獲。發(fā)現(xiàn)了一些黑盒難以發(fā)現(xiàn)的問題,以及開發(fā)的代碼夾帶,并且對于這個版本影響范圍的評估也更準確。但問題是短期會花費測試更多時間,以及需要測試人員有一定的技術能力。