第五次重構(gòu)我們引入了數(shù)據(jù)庫(kù)的設(shè)計(jì),用戶信息要從數(shù)據(jù)庫(kù)中讀取,問(wèn)候語(yǔ)庫(kù)存儲(chǔ)在數(shù)據(jù)庫(kù)中,并支持添加與更新。數(shù)據(jù)庫(kù)的引入使自動(dòng)化測(cè)試變得困難了,因?yàn)閿?shù)據(jù)狀態(tài)總是變化著的,而這種變化使得測(cè)試過(guò)程不能復(fù)現(xiàn),這是我們不愿看到的。因此,我們?cè)谠O(shè)計(jì)時(shí)將業(yè)務(wù)與數(shù)據(jù)庫(kù)訪問(wèn)分離,形成了UserDao與GreetingRuleDao。此時(shí),我們的設(shè)計(jì)應(yīng)當(dāng)遵從“依賴反轉(zhuǎn)”原則,即將UserDao與GreetingRuleDao設(shè)計(jì)成接口,并編寫它們的實(shí)現(xiàn)UserDaoImpl與GreetingRuleDaoImpl。這樣設(shè)計(jì)就為我們Mock掉UserDao與GreetingRuleDao的實(shí)現(xiàn)類創(chuàng)造了條件。
這是我們的設(shè)計(jì):
為此,我們編寫了這樣的測(cè)試程序:
這段測(cè)試程序比較長(zhǎng),但其它部分都是打醬油的,核心是那個(gè)testSayHelloInTheMorning()用例,即問(wèn)候早上好這個(gè)用例。userDao與greetingRuleDao是兩個(gè)接口,我們?cè)趯?shí)例化它們的時(shí)候,并沒(méi)有去創(chuàng)建它們的實(shí)現(xiàn)類,而是用Mock的方式進(jìn)行創(chuàng)建:
隨后我們開(kāi)始定義它們的行為loadUser()與getAllGreetingRules()。在這個(gè)測(cè)試用例中,我們并不關(guān)心它們是怎樣去數(shù)據(jù)庫(kù)里查詢數(shù)據(jù)并返回的,我們只關(guān)心它們是否得到應(yīng)該得到的參數(shù),并要求它們按照規(guī)定返回一個(gè)結(jié)果:
我們希望被測(cè)程序在執(zhí)行的時(shí)候調(diào)用了userDao.loadUser(userId),并且調(diào)用時(shí)傳入的參數(shù)userId = 2013090701。如果測(cè)試過(guò)程中傳入的參數(shù)是這個(gè)值,這一項(xiàng)檢查點(diǎn)可以通過(guò),否則不能通過(guò)。隨后我們希望該函數(shù)返回“鮑曉妹”這個(gè)用戶對(duì)象。通過(guò)Mock程序,我們完全將數(shù)據(jù)庫(kù)訪問(wèn)的過(guò)程剝離在自動(dòng)化測(cè)試之外,而只是驗(yàn)證它的輸入?yún)?shù),并指定測(cè)試所需的返回結(jié)果。也就是說(shuō)數(shù)據(jù)訪問(wèn)過(guò)程被Mock掉,而大大降低了測(cè)試難度。
如果UserDao與GreetingRuleDao的Mock程序不能得到規(guī)定的參數(shù)時(shí),測(cè)試就不能通過(guò),這就是說(shuō)傳遞給Mock程序的參數(shù)也成為了測(cè)試程序要驗(yàn)證的一個(gè)輸出。隨后,Mock程序返回規(guī)定值,該規(guī)定值則成為了被測(cè)程序的一個(gè)輸入。最后,被測(cè)程序根據(jù)這個(gè)輸入返回結(jié)果,為測(cè)試程序所驗(yàn)證,測(cè)試結(jié)束(如圖4.4所示)。
圖中的BUS層才是我們大量編碼,應(yīng)當(dāng)自動(dòng)化測(cè)試的部分。既然是測(cè)試就是驗(yàn)證怎樣的輸入,應(yīng)當(dāng)?shù)玫皆鯓拥妮敵觥eb層向BUS層發(fā)出的請(qǐng)求,即調(diào)用BUS層某個(gè)類的方法,就是測(cè)試用例中的一個(gè)輸入,執(zhí)行完該方法后的返回值就是測(cè)試用例的一個(gè)輸出。但是,這對(duì)輸入輸出并不是該測(cè)試用例的全部。這里經(jīng)過(guò)BUS層處理以后,經(jīng)過(guò)一系列的邏輯判斷和數(shù)據(jù)操作,隨后會(huì)去調(diào)用DAO層進(jìn)行數(shù)據(jù)訪問(wèn)操作。調(diào)用DAO層時(shí)所傳遞的參數(shù),就是測(cè)試用例的另一個(gè)輸出。圖中,從DAO層的輸入,到它的輸出,這段數(shù)據(jù)庫(kù)訪問(wèn)的過(guò)程被Mock程序Mock掉了,因?yàn)樗辉谶@個(gè)用例的測(cè)試范圍。然后DAO層返回給BUS層一個(gè)結(jié)果,該結(jié)果就是測(cè)試用例的另一個(gè)輸入。接著B(niǎo)US層會(huì)再次對(duì)這個(gè)返回結(jié)果進(jìn)行處理,最后返回給Web層最終的結(jié)果。這就是采用Mock方式進(jìn)行自動(dòng)化測(cè)試的一個(gè)完整流程。
采用自動(dòng)化測(cè)試,測(cè)試程序?qū)⒉辉衮?yàn)證后臺(tái)的數(shù)據(jù)庫(kù)是否正確,同時(shí)也不再驗(yàn)證前臺(tái)的Web應(yīng)用及其前端設(shè)備是否正確。在該例中,系統(tǒng)的真正目的是要在前臺(tái)顯示對(duì)用戶的問(wèn)候,因此將會(huì)有一個(gè)Action或Servlet調(diào)用HelloWorld:
然而,這些程序都不適合自動(dòng)化測(cè)試而應(yīng)采用手工測(cè)試。回顧HelloWorld自動(dòng)化測(cè)試建立的過(guò)程我們不難發(fā)現(xiàn),它在設(shè)計(jì)之初就實(shí)現(xiàn)了業(yè)務(wù)邏輯與Web應(yīng)用、與數(shù)據(jù)訪問(wèn)的分離,所以它可以輕易的建立自動(dòng)化測(cè)試。但是,不幸的是,我們大多數(shù)的遺留系統(tǒng)在設(shè)計(jì)之初都沒(méi)有考慮到這些。因此,我說(shuō),在重構(gòu)之初首先建立自動(dòng)化測(cè)試機(jī)制是不現(xiàn)實(shí)的,我們只能采用手工測(cè)試結(jié)合QTP的方式。只有當(dāng)我們通過(guò)重構(gòu),使系統(tǒng)架構(gòu)滿足自動(dòng)化測(cè)試的條件之后,自動(dòng)化測(cè)試才可以開(kāi)展。
毫無(wú)疑問(wèn),測(cè)試與重構(gòu)形成了一個(gè)“雞生蛋,還是蛋生雞”的怪圈,成為我們實(shí)踐系統(tǒng)重構(gòu)一大攔路虎。本書(shū)將在后面詳細(xì)討論這個(gè)話題(詳見(jiàn) 第十六章 測(cè)試的困境),為你破解這個(gè)謎團(tuán)。
大話重構(gòu)連載首頁(yè): http://fangang.iteye.com/blog/2081995
特別說(shuō)明:希望網(wǎng)友們?cè)谵D(zhuǎn)載本文時(shí),應(yīng)當(dāng)注明作者或出處,以示對(duì)作者的尊重,謝謝!
這是我們的設(shè)計(jì):
圖4.3 HelloWorld的設(shè)計(jì)圖
為此,我們編寫了這樣的測(cè)試程序:
private HelloWorld helloWorld = null; private GreetingToUserImpl greetingToUser = null; private GreetingAboutTimeImpl greetingAboutTime = null; private final static List<GreetingRule> GREETING_RULES = getRules(); /** * @throws java.lang.Exception */ @Before public void setUp() throws Exception { helloWorld = new HelloWorld(); greetingToUser = new GreetingToUserImpl(); greetingAboutTime = new GreetingAboutTimeImpl(); helloWorld.setGreetingToUser(greetingToUser); helloWorld.setGreetingAboutTime(greetingAboutTime); } /** * @throws java.lang.Exception */ @After public void tearDown() throws Exception { helloWorld = null; greetingToUser = null; greetingAboutTime = null; } /** * Test method for {@link org...HelloWorld#sayHello(java.util.Date, java.lang.String)}. */ @Test public void testSayHelloInTheMorning() { final Date now = DateUtil.createDate(2013, 9, 7, 9, 23, 11); final long userId = 2013090701; UserDao userDao = createMock(UserDao.class); GreetingRuleDao greetingRuleDao = createMock(GreetingRuleDao.class); expect(userDao.loadUser(userId)).andAnswer(new IAnswer<User>(){ @Override public User answer() throws Throwable { User user = new User(); user.setUserId(userId); user.setName("鮑曉妹"); return user; }}); expect(greetingRuleDao.findAllGreetingRules()) .andAnswer(new IAnswer<List<GreetingRule>>(){ @Override public List<GreetingRule> answer() throws Throwable { return GREETING_RULES; }}); replay(userDao); replay(greetingRuleDao); greetingToUser.setUserDao(userDao); greetingAboutTime.setGreetingRuleDao(greetingRuleDao); String result = helloWorld.sayHello(now, userId); Assert.assertEquals("Hi, 鮑曉妹. Good morning!", result); verify(userDao); verify(greetingRuleDao); }
這段測(cè)試程序比較長(zhǎng),但其它部分都是打醬油的,核心是那個(gè)testSayHelloInTheMorning()用例,即問(wèn)候早上好這個(gè)用例。userDao與greetingRuleDao是兩個(gè)接口,我們?cè)趯?shí)例化它們的時(shí)候,并沒(méi)有去創(chuàng)建它們的實(shí)現(xiàn)類,而是用Mock的方式進(jìn)行創(chuàng)建:
UserDao userDao = createMock(UserDao.class); GreetingRuleDao greetingRuleDao = createMock(GreetingRuleDao.class);
隨后我們開(kāi)始定義它們的行為loadUser()與getAllGreetingRules()。在這個(gè)測(cè)試用例中,我們并不關(guān)心它們是怎樣去數(shù)據(jù)庫(kù)里查詢數(shù)據(jù)并返回的,我們只關(guān)心它們是否得到應(yīng)該得到的參數(shù),并要求它們按照規(guī)定返回一個(gè)結(jié)果:
final long userId = 2013090701; expect(userDao.loadUser(userId)).andAnswer(new IAnswer<User>(){ @Override public User answer() throws Throwable { User user = new User(); user.setUserId(userId); user.setName("鮑曉妹"); return user; }});
我們希望被測(cè)程序在執(zhí)行的時(shí)候調(diào)用了userDao.loadUser(userId),并且調(diào)用時(shí)傳入的參數(shù)userId = 2013090701。如果測(cè)試過(guò)程中傳入的參數(shù)是這個(gè)值,這一項(xiàng)檢查點(diǎn)可以通過(guò),否則不能通過(guò)。隨后我們希望該函數(shù)返回“鮑曉妹”這個(gè)用戶對(duì)象。通過(guò)Mock程序,我們完全將數(shù)據(jù)庫(kù)訪問(wèn)的過(guò)程剝離在自動(dòng)化測(cè)試之外,而只是驗(yàn)證它的輸入?yún)?shù),并指定測(cè)試所需的返回結(jié)果。也就是說(shuō)數(shù)據(jù)訪問(wèn)過(guò)程被Mock掉,而大大降低了測(cè)試難度。
如果UserDao與GreetingRuleDao的Mock程序不能得到規(guī)定的參數(shù)時(shí),測(cè)試就不能通過(guò),這就是說(shuō)傳遞給Mock程序的參數(shù)也成為了測(cè)試程序要驗(yàn)證的一個(gè)輸出。隨后,Mock程序返回規(guī)定值,該規(guī)定值則成為了被測(cè)程序的一個(gè)輸入。最后,被測(cè)程序根據(jù)這個(gè)輸入返回結(jié)果,為測(cè)試程序所驗(yàn)證,測(cè)試結(jié)束(如圖4.4所示)。
圖4.4 HelloWorld的自動(dòng)化測(cè)試
圖中的BUS層才是我們大量編碼,應(yīng)當(dāng)自動(dòng)化測(cè)試的部分。既然是測(cè)試就是驗(yàn)證怎樣的輸入,應(yīng)當(dāng)?shù)玫皆鯓拥妮敵觥eb層向BUS層發(fā)出的請(qǐng)求,即調(diào)用BUS層某個(gè)類的方法,就是測(cè)試用例中的一個(gè)輸入,執(zhí)行完該方法后的返回值就是測(cè)試用例的一個(gè)輸出。但是,這對(duì)輸入輸出并不是該測(cè)試用例的全部。這里經(jīng)過(guò)BUS層處理以后,經(jīng)過(guò)一系列的邏輯判斷和數(shù)據(jù)操作,隨后會(huì)去調(diào)用DAO層進(jìn)行數(shù)據(jù)訪問(wèn)操作。調(diào)用DAO層時(shí)所傳遞的參數(shù),就是測(cè)試用例的另一個(gè)輸出。圖中,從DAO層的輸入,到它的輸出,這段數(shù)據(jù)庫(kù)訪問(wèn)的過(guò)程被Mock程序Mock掉了,因?yàn)樗辉谶@個(gè)用例的測(cè)試范圍。然后DAO層返回給BUS層一個(gè)結(jié)果,該結(jié)果就是測(cè)試用例的另一個(gè)輸入。接著B(niǎo)US層會(huì)再次對(duì)這個(gè)返回結(jié)果進(jìn)行處理,最后返回給Web層最終的結(jié)果。這就是采用Mock方式進(jìn)行自動(dòng)化測(cè)試的一個(gè)完整流程。
采用自動(dòng)化測(cè)試,測(cè)試程序?qū)⒉辉衮?yàn)證后臺(tái)的數(shù)據(jù)庫(kù)是否正確,同時(shí)也不再驗(yàn)證前臺(tái)的Web應(yīng)用及其前端設(shè)備是否正確。在該例中,系統(tǒng)的真正目的是要在前臺(tái)顯示對(duì)用戶的問(wèn)候,因此將會(huì)有一個(gè)Action或Servlet調(diào)用HelloWorld:
Date now = DateUtil.getNow(); String user = SessionUtil.getCurrentUser(session); HelloWorld helloWorld = new HelloWorld(); String greeting = helloWorld.sayHello(now, user); request.setAttribute(“greeting”, greeting);
然而,這些程序都不適合自動(dòng)化測(cè)試而應(yīng)采用手工測(cè)試。回顧HelloWorld自動(dòng)化測(cè)試建立的過(guò)程我們不難發(fā)現(xiàn),它在設(shè)計(jì)之初就實(shí)現(xiàn)了業(yè)務(wù)邏輯與Web應(yīng)用、與數(shù)據(jù)訪問(wèn)的分離,所以它可以輕易的建立自動(dòng)化測(cè)試。但是,不幸的是,我們大多數(shù)的遺留系統(tǒng)在設(shè)計(jì)之初都沒(méi)有考慮到這些。因此,我說(shuō),在重構(gòu)之初首先建立自動(dòng)化測(cè)試機(jī)制是不現(xiàn)實(shí)的,我們只能采用手工測(cè)試結(jié)合QTP的方式。只有當(dāng)我們通過(guò)重構(gòu),使系統(tǒng)架構(gòu)滿足自動(dòng)化測(cè)試的條件之后,自動(dòng)化測(cè)試才可以開(kāi)展。
毫無(wú)疑問(wèn),測(cè)試與重構(gòu)形成了一個(gè)“雞生蛋,還是蛋生雞”的怪圈,成為我們實(shí)踐系統(tǒng)重構(gòu)一大攔路虎。本書(shū)將在后面詳細(xì)討論這個(gè)話題(詳見(jiàn) 第十六章 測(cè)試的困境),為你破解這個(gè)謎團(tuán)。
大話重構(gòu)連載首頁(yè): http://fangang.iteye.com/blog/2081995
特別說(shuō)明:希望網(wǎng)友們?cè)谵D(zhuǎn)載本文時(shí),應(yīng)當(dāng)注明作者或出處,以示對(duì)作者的尊重,謝謝!
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺(jué)我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長(zhǎng)非常感激您!手機(jī)微信長(zhǎng)按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
