最近一階段,由于項目的需要,粗略的看了看測試用到的幾種framework和tools,胡亂寫了些東西,主要針對自己對于這幾種framework的理解,這里只起到拋磚引玉,具體細(xì)節(jié)請查看每個framework自己的官方網(wǎng)站
啊?“測試”有這么重要?
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
???????
???????
沒有進(jìn)入
TC
前,在自己做過的一些小型項目中(或者根本不能稱其為項目,只不過是自己寫的一些程序)基本沒有做過比較體系的測試,更不用提應(yīng)用一些
framework
或是一些工具來進(jìn)行測試,那時候的測試往往就是在一定的位置上
print
出結(jié)果,與預(yù)期的結(jié)果相比較,從而知道程序的對錯。進(jìn)入
TC
后,發(fā)現(xiàn)每一個開發(fā)團(tuán)隊都有各自的
QA
,而且有些團(tuán)隊
QA
的人數(shù)并不少于開發(fā)人員,測試的重要性在這里可見一斑。閱讀了一些關(guān)于測試的文章,尤其是
X-Programming
的一些材料后,才認(rèn)識到測試工作如此重要。“
Program a little, test little
”
,
貌似平淡無奇的一句話,但卻是所有項目尤其是企業(yè)級項目成功的保證。從某種程度上說,
QA
肩負(fù)著比開發(fā)人員更重的使命。
???????
鑒于測試有如此重要的地位,而且我有可能(只是有可能而已)參與
GTSS
的測試項目,所以近一個星期以來一直在對“測試”進(jìn)行研究。粗略的學(xué)習(xí)了一下用于測試的一些
framework
和工具,其中包括
JUnit, JMeter, HttpUnit, JWebUnit,
并下
Fund Connect
近期完成的
Performance Test
進(jìn)行了研究。
?
想把學(xué)到的東西給大家看看
?
???????
我的習(xí)慣是把一個階段學(xué)習(xí)到的東西總結(jié)一下,將其變成文字,這樣才能對學(xué)到的東西加深印象,也在成文過程中發(fā)現(xiàn)不明白不理解的地方。往往我寫出的東西,都是給自己看的,加深個印象也就算了,但是這次為什么想和大家分享呢?并非炫耀自己有多么能干,學(xué)了多少東西,因為我深知看我這篇東西的人各個都比我能干,每一個人懂得都比我多。主要原因是:和大家不同,我不是做技術(shù)出身,只是單純對技術(shù)有興趣,想深入了解技術(shù),由于專業(yè)知識的欠缺,肯定在學(xué)習(xí)和應(yīng)用過程中會出現(xiàn)很多偏差,理解上也會有很多不對的地方,希望大家及時指出,我好及時更正。
??????
好了不多羅嗦了,開始正題!(歡迎大家拍轉(zhuǎn))
?
JUnit
單元測試的基礎(chǔ)
?
???????
我可以大言不慚地說,
JUnit
本身是一個相當(dāng)簡單的框架,想必各位也對其做過一些研究,所以直接給出一個自己寫的簡單的例子,來說明
JUnit
的特點。
?
//
Money.java:
?
public
?
class
?Money?
{
????????
private
?
int
?amount;
????????
????????
public
?Money(
int
?i)
{
???????????????amount?
=
?i;
????????}
????????
????????
public
?
int
?amount()
{
???????????????
return
?amount;
????????}
????????
public
?Money?addMoney(Money?mon)
{
???????????????
return
?
new
?Money(mon.amount()
+
this
.amount());
???????????????
????????}
//
The?key?point?here
????????
public
?boolean?equals(Object?o)
{
???????????????
if
(o?instanceof?Money)
???????????????????????
return
?((Money)o).amount()?
==
?
this
.amount();
???????????????
else
?
???????????????????????
return
?
false
;
????????}
}
?
?
//
TestMoney.java
?
import?junit.framework.
*
;
?
public
?
class
?TestMoney?extends?TestCase
{
????????Money?m11;
????????Money?m12;
????????Money?m13;
????????
protected
?
void
?setUp()
{
???????????????m11?
=
?
new
?Money(
11
);
???????????????m12?
=
?
new
?Money(
12
);
???????????????m13?
=
?
new
?Money(
13
);
????????}
????????
????????
public
?
void
?testEquals()
{
???????????????Assert.assertEquals(m11,
new
?Money(
11
));
???????????????
???????????????Assert.assertEquals(m11,
new
?Money(
11
));
????????}
????????
????????
public
?
void
?testAdd()
{
???????????????Money?m23?
=
?
new
?Money(
23
);
???????????????Assert.assertEquals(m11.addMoney(m12),m23);
???????????????Assert.assertEquals(m11.addMoney(m12),m11);
???????????????
????????}
????????
????????
public
?
static
?
void
?main(String?args[])?
{
???????????????junit.textui.TestRunner.run(TestMoney.
class
);
????????}
????????
}
???????
看到這里你可能會罵我把小孩子都能寫出的東西貼到這里來丟人,其實就是這么個簡單的例子足以說明
JUnit
的運作原理。
class Money
重載了方法
equals(),
就是為了進(jìn)行
Money
對象之間的比較。這樣的比較在
JUnit
中是通過斷言的方式進(jìn)行的,由于基礎(chǔ)類
TestCase
繼承于
Assert
類,從而繼承了
Assert
類提供的所有斷言方法。所以一句話概括,
JUnit
是通過斷言機(jī)制進(jìn)行底層對象間的比較來判斷功能正確與否的。你可能會抬杠的說:“不僅是對象吧,
JUnit
也可以比較兩個
int
或者其他的
primitive data
啊!”,但在
OO
的理論中,
Java
中的
primitive data
也應(yīng)該是對象(如
SmallTalk
中的實現(xiàn)),但
Java
出于對性能的考慮,對
primitive data
沒有采取類的實現(xiàn)方式,但同時也給出了各個
primitive data
的
wrapper class
。
最初認(rèn)識到
JUnit
的這樣的工作原理,我有些失望懷疑它能否勝任復(fù)雜的商業(yè)邏輯的測試,看到了
Fund Connect
的
performance test
中測試
service
部分的代碼,我這樣的疑慮被消除了。下面節(jié)選一段代碼說明:
public
?
class
?AdminServiceTest?extends?TestCase
{
????????
private
?
static
?Log?log?
=
?LogFactory.getLog(AdminServiceTest.
class
);
????????
private
?
static
?ServiceFactory?factory;
????????
protected
?
static
?AdminService?ds;
????????
private
?
static
?ServiceConfigurator?serviceConfig;
????????
private
?Statement?stmt;
????????
private
?ResultSet?rs;
????????
private
?String?sConnStr;
????????
private
?String?sqlStr;
????????
private
?Connection?conn
=
null
;
????
/**/
/*
*
?????*?Constructor?for?AdminServiceImplTest.
?????*?@param?arg0
?????
*/
????
public
?AdminServiceTest(String?arg0)
????
{
????????super(arg0);
????}
????
?????????????
protected
?
void
?setUp()?throws?Exception
????????????
{
?????????????????
//
……
?????????????}
?????????????
protected
?
void
?teardown()?throws?Exception
?????????????
{
?????????????????
//
?…..
?????????????}
?
?????????????
public
?
void
?testGetFundProcessors()
????????????
{
????????
sqlStr?
=
?
"
select?distinct?Fund_Processor_uid?from???????????????????????????????FUND_PROCESSOR_INSTR_XREF?
"
;
???????????????
try
?
{
???????????????????????rs?
=
?stmt.executeQuery(sqlStr);
???????????????????????List?result?
=
?(List)ds.getFundProcessors();
???????????????????????
int
?i?
=
?
0
;
???????????????????????
while
(rs.next())
{
???????????????????????????????i?
++
;
???????????????????????}
???????????????????????assertEquals(i,?result.size());
???????????????????????
???????????????}
?
catch
?(SQLException?e)?
{
???????????????????????e.printStackTrace();
???????????????}
?
catch
?(AdminServiceException?e)?
{
???????????????????????e.printStackTrace();
???????????????}
???????
???????????????}
???????????????
//
……
????????????}
?
??
??????
從這個例子中可以看到,無論是多么復(fù)雜的邏輯(
testGetFundProcessors
)最終都能轉(zhuǎn)化成底層對象通過斷言的比較(紅色字體部分)。“
Everything is object.
”
, JUnit
的工作原理決定了它應(yīng)該是單元測試的基礎(chǔ)。
????????
另外,我也看了一下
JUnit
的源碼,代碼并不是很多多,由于應(yīng)用了一些模式,使其結(jié)構(gòu)設(shè)計較好。例如:
TestCase
類中,在設(shè)計
run
()方法的繼承問題時,應(yīng)用了
Template Method Pattern
;
對于多個
test
方法,要有針對性地生成相應(yīng)的
TestCase
,應(yīng)用了
Adapter Pattern
;等等。大家有興趣的,可以對其源碼進(jìn)行研究。
?
強(qiáng)大的測試工具
JMeter
???????
我看過的所有的
Apache
的項目,都很成功。
JMeter
也不例外,說其強(qiáng)大,我個人認(rèn)為有以下三個原因:
1、
較為友好的圖形用戶界面,易于測試人員使用,只要明白其中的原理用
JMeter
作測試是件愉快的事情而且它能夠方便的生成測試腳本。
2、
ThreadGroup
概念的引進(jìn),這個概念在
JMeter
是相當(dāng)重要的,之所以
JMeter
能夠完成對各種不同服務(wù)器的壓力測試與性能測試,也仰仗著
ThreadGroup
。在一個
ThreadGoup
中可以規(guī)定應(yīng)用的線程數(shù)量(
Number of Threads
每一個線程代表一個用戶),也可以規(guī)定用戶行為的重復(fù)次數(shù)
(loop count)
。在企業(yè)級應(yīng)用的測試中,模擬多個用戶同時執(zhí)行操作或同時處理數(shù)據(jù)的操作,利用
ThreadGroup
可以輕松實現(xiàn)。
3、
JMeter
將大量的
Test Target
作了非常好的封裝
,
用戶可以直接使用這些封裝好的部件
,
大大減少了測試的工作量。比如測試
WebService
用的
WebService SOAP Request
,
測試網(wǎng)頁的
HttpRequest
,測試數(shù)據(jù)庫連接的
JDBC Request
等等。測試工作進(jìn)而簡化成了選擇組建,將各個組建有邏輯的組織在一起的過程。
???????
對于
JMeter
的強(qiáng)大以及其應(yīng)用方法,因為大家都懂,所以我在這里不多說了。下面談?wù)剛€人認(rèn)為
JMeter
的不足之處。為了更清楚的說明這個問題,我將結(jié)合
Fund Connect
項目中,對于
Server Performance Test
的一些
JMeter
腳本進(jìn)行闡述。其中的一個
Requirement
如下:
?
Investors submit trades via GUI
?
Description
: 15 investors log into FundConnect via GUI. Each investor submits 20 orders
via a file upload.
Precondition: 20 investor institutions and 20 investors (one investor per investor institution) are set up in FundConnect.
Step:
A JMeter script will execute the following steps: Log In
à
Start in Investor Home
à
Upload Multiple Orders
à
Select upload file
à
Submit file
à
Return to Investor Home.
Note:
Each investor should upload a different file. The script should record the average time to execute the entire loop (Investor Home to Investor Home).
?
???????
想必大家對這個需求再清楚不過了(畢竟剛完成測試工作)。用
JMeter
測試中有一個關(guān)鍵的組件(其余省略不說了):
HttpRequest
Name
:
/fundconnect/FCUploadOrdersServlet
?
Send Parameters With the Request
Name
???????????????????????
value
thisURL
???????????????????
/inv/upload_orders.jsp
submitURL
?????????????
/inv/upload_orders_summary.jsp
cancelURL
?????????????
/adm/index.jsp
?
Send a file with request
File name: D:\datatest\datatest\20order_upload\testdata\uploadorders_20_acct1.csv
Parameter Name: uploadfile
MIME Type: application/octet-stream
?
????
這個
Request
是向
FCUploadOrdersServlet
發(fā)出,其間傳遞了三個參數(shù)和一個文件,完成上傳
order
文件的工作。這個需求到此也就結(jié)束了,但大家有沒有想過,如果把需求改成
: 15 investors log into FundConnect via GUI. Each investor submits 20 orders
via web page.
即如果要求這
15
個投資者通過
web
(非上傳文件方式)遞交
20
不同的
order
,模擬這樣的測試該如何進(jìn)行呢?
JMeter
針對這樣的
Web
頁面操作的測試,實現(xiàn)起來比較復(fù)雜。(如果誰認(rèn)為不對,可以聯(lián)系我,把你的方法告訴我)。我個人認(rèn)為做
Web Application
頁面上的功能測試,使用下面談到的兩個框架,實現(xiàn)起來比較簡單。
?
Web Application
自動化測試
FrameWorks
:
HttpUnit
???
JWebUnit
?
HttpUnit
本身并沒有測試功能
,
說白了
,
它不過包含了一些類庫
,
可以用來模擬出一個瀏覽器
(
WebConversation
類
)
并可以模擬用戶在網(wǎng)頁上的多種行為。
HttpUnit
沒有測試的功能,所以它要結(jié)合
JUnit
來完成
Web
測試的工作,例如下面是一段簡單的代碼:
import?junit.framework.TestCase;
import?com.meterware.httpunit.WebResponse;
import?com.meterware.httpunit.WebConversation;
import?com.meterware.httpunit.WebForm;
import?com.meterware.httpunit.WebRequest;
?
public
?
class
?SearchExample?extends?TestCase?
{
?
???
public
?
void
?testSearch()?throws?Exception?
{
??????
//
模擬瀏覽器
??????WebConversation?wc?
=
?
new
?WebConversation();
??????
//
對google主頁發(fā)出request,并得到response
??????WebResponse?resp?
=
?wc.getResponse(?
"
http://www.google.com
"
);
??????
//
從Response中抽取出第一個table
??????WebForm?form?
=
?resp.getForms()[
0
];
??????
//
在搜索的text?field?called?q中填寫”HttpUnit”
??????form.setParameter(
"
q
"
,?
"
HttpUnit
"
);
??????
//
點擊提交按鈕
??????WebRequest?req?
=
?form.getRequest(
"
btnG
"
);
??????resp?
=
?wc.getResponse(req);
??????
//
通過反饋回來的response來判斷叫做HttpUnit的link是否存在
??????assertNotNull(resp.getLinkWith(
"
HttpUnit
"
));
??????
//
模擬點擊連接的功能
??????resp?
=
?resp.getLinkWith(
"
HttpUnit
"
).click();
??????
//
通過title來判斷返回的reponse的title是否為HttpUnit
??????assertEquals(resp.getTitle(),?
"
HttpUnit
"
);
??????assertNotNull(resp.getLinkWith(
"
User's?Manual
"
));
???}
}
你可以發(fā)現(xiàn)上面的一個例子,
已經(jīng)完成了對于在
google
中查找
HttpUnit
關(guān)鍵字的測試。
在此基礎(chǔ)上,
JWebUnit
更近一步,它實際上是建立在
HttpUnit
和
JUnit
框架之上,將二者功能結(jié)合、重構(gòu)后的產(chǎn)物。同時,
JWebUnit
提供了更加易用的
API
來模擬用戶對
web
界面的操作,同樣是上面的代碼,
JWebUnit
的實現(xiàn)如下
:
import?net.sourceforge.jwebunit.WebTestCase;
?
public
?
class
?JWebUnitSearchExample?extends?WebTestCase?
{
?
???
public
?JWebUnitSearchExample(String?name)?
{
??????super(name);
???}
?
???
public
?
void
?setUp()?
{
??????getTestContext().setBaseUrl(
"
http://www.google.com
"
);
???}
?
???
public
?
void
?testSearch()?
{
??????beginAt(
"
/
"
);
??????setFormElement(
"
q
"
,?
"
httpunit
"
);
??????submit(
"
btnG
"
);
??????clickLinkWithText(
"
HttpUnit
"
);
??????assertTitleEquals(
"
HttpUnit
"
);
??????assertLinkPresentWithText(
"
User's?Manual
"
);
???}
}
?????
我相信不用加任何注釋
,
大家也可以輕松的理解每一步操作。接下來我應(yīng)用了
JWebUnit
測試了
Fund Connect web
頁面上的一些功能
,
下面列出了以
Admin
身份登陸
,
對增加
Fund
這樣一個功能的測試
:
?
import?net.sourceforge.jwebunit.WebTestCase;
import?org.xml.sax.SAXException;
import?com.meterware.httpunit.HttpUnitOptions;
import?com.meterware.httpunit.WebConversation;
import?com.meterware.httpunit.WebResponse;
?
public
?
class
?AdminTest?extends?WebTestCase
{
????
public
?
static
?
void
?main(String?[]?args)
{
???????????????junit.swingui.TestRunner.run(AdminTest.
class
);
????????}
????????
public
?
void
?setUp()
{
???????????????
//
get?rid?of?Java?Script?check
???????????????HttpUnitOptions.setExceptionsThrownOnScriptError(
false
);
???????????????
//
set?the?base?url?for?this?test
???????????????getTestContext().setBaseUrl(
"
http://tcsunfire04.zdus.com:9220
"
);
???????????????
//
set?username?and?password?to?get?through?SiteMinder?????????authentication
???????????????getTestContext().setAuthorization(
"
bos.ssb.dwf
"
,?
"
123
"
);
???????????????
//
set?the?cookie?required
????????????????getTestContext().addCookie(
"
SMCHALLENGE
"
,?
"
YES
"
);
?
????????}
???????????????
????????
public
?
void
?testSiteMinder()
{
???????????????
//
test?wether?test?can?get?through?SiteMinder?or?not
???????????????beginAt(
"
/fundconnect/adm
"
);
???????????????assertTitleEquals(
"
Global?Link?Fund?Connect
"
);
????????}
????????
/**/
/*
?????????*Test?for?adding?a?new?Fund
?????????*Fund?long?name:?star's?Fund
?????????*Fund?short?name:?starFund
?????????*Fund?Provider:?Starhero
?????????
*/
????????
????????
public
?
void
?testAddFund()
{
???????????????beginAt(
"
/fundconnect/adm/maintfunds_funddet.jsp?add=new
"
);
???????????????
//
Fill?in?the?add?fund?form
???????????????setFormElement(
"
fundInstrumentLongName
"
,
"
star's?fund
"
);
???????????????setFormElement(
"
fundInstrumentName
"
,
"
starFund
"
);
???????????????setFormElement(
"
fundInstrumentCode
"
,
"
123567
"
);
???????????????selectOption(
"
fundCodeType
"
,
"
ISO
"
);
???????????????selectOption(
"
timeZone
"
,
"
(GMT+08:00)?Asia/Shanghai
"
);
???????????????selectOption(
"
fundProviderIndex
"
,
"
Starhero
"
);
???????????????selectOption(
"
settlementCurrency
"
,
"
USD?--?US?Dollar
"
);
???????????????selectOption(
"
partialShares
"
,
"
No
"
);
???????????????setFormElement(
"
contactName
"
,
"
Brooks
"
);
???????????????setFormElement(
"
contactPhoneNumber
"
,
"
13989472700
"
);
???????????????selectOption(
"
investmentType
"
,
"
Short?Term
"
);
???????????????selectOption(
"
assetClass
"
,
"
EQUITY
"
);
???????????????selectOption(
"
industry
"
,
"
DEVELOPED
"
);
???????????????selectOption(
"
countryRegion
"
,
"
UNITED?STATES
"
);
???????????????selectOption(
"
benchmark
"
,
"
AUD?LIBID
"
);
???????????????selectOption(
"
domicileCountry
"
,
"
United?States
"
);
???????????????setFormElement(
"
defaultPrice
"
,
"
50
"
);
???????????????
???????????????selectOption(
"
fundInstrumentCountries
"
,
"
United?States
"
);
???????????????selectOption(
"
institutionSelect
"
,
"
lon.ssb
"
);
???????????????????????
???????????????setFormElement(
"
cutoff
"
,
"
22:00
"
);
???????????????selectOption(
"
cutoffType
"
,
"
Hard?Cutoff
"
);
???????????????
//
submit?the?form
???????????????submit(
"
sbmtSubmit
"
);
????????????????
//
According?to?the?fund's?long?name?and?fund's?short?name?to?assert
????????????????
//
that?fund?is?added
????????????????assertTextPresent(
"
star's?fund
"
);
????????????????assertTextPresent(
"
starFund
"
);
???????????????
????????}
????
}
?
???????
由此看出
, JWebUnit
可以完成
Web
頁面上復(fù)雜應(yīng)用的測試。可以在以后的項目中逐漸使用。
?
?
寫在后面
?????
?
???????
首先得感謝你,能夠耐得住性子看到這里,浪費了你寶貴的時間深表歉意。以上就是我對近期對于測試工具及
FrameWork
研究的小結(jié),有不對或不妥之處還請指正。
?
?
?
?
?
?
?
?
?
?
?
?
?
?
?
??????
????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????
小川
?????????
<?xml:namespace prefix = st1 ns = "urn:schemas-microsoft-com:office:smarttags" /><date year="2005" day="12" month="7">7/12/2005</date>
幾個測試用到的framework和tools