grails學習(二)grails單元測試

          以前我在小公司,完成項目功能是終極目標。開發人員很害怕需求變化,因為他們改怕了。那問題出在哪里呢?后來我仔細想想,是沒有做測試造成。那開發人員為什么如此害怕需求變化,我舉個例子,a服務給b服務和c服務調用,后來需求改變,導致a服務無法滿足b服務,能完成自身的功能是天大的事,于是沒有和別人溝通把a服務直接改了。項目上線,突然有一天客戶打電話說你們網站這里出問題,那里出問題,以前都不會的啊。你們怎么弄的。于是根據頁面錯誤信息,開發人員很快找到錯誤根源,原來a服務改動,導致b服務不正常。而d,e,f服務依賴于b,那么導致d,e,f相關功能都出錯了。立馬動手改,改完上線,能知道的問題都沒了,哈哈,真高興,可是不能高興太早哇,也許還有潛在bug。

          軟件的bug是無法避免,但是我們可以盡量減少bug,不斷提升代碼質量。剛我也說過,上述問題造成的原因是沒有做測試。測試包括很多了,單元測試、集成測試和功能測試等等。既然測試如此重要,每完成一個類都能進行測試。

          以前也許你比較糾結,沒有好的工具,現在java社區非常活躍,我們可以選擇的太多太多了:junit4,jmock,mockito,easymock,TestNg等等。如果你用過grails,那么你更清楚,此類快速開發框架已經幫我們集成好了。使用起來非常簡單。所以今天我主要講述下grails的單元測試。

          假設需求:我們給每個用戶分配工作,每個人都要完成兩件事情,第一件事情:根據自己的用戶名返回歡迎信息;第二件事情:根據自己的地址返回國家地區。

          詳細設計

          用戶信息類:
          package com.test.domian

          class User {

              
          int id
              String name
              String address
              
              
          static constraints = {
              }
          }

          工作服務接口:
          package com.test.services

          class WorkService {

              
          /**
               * 根據用戶名返回歡迎字符
               * 
          @param userName
               * 
          @return
               
          */
              def processWorkOne(String userName) {
              }
              
              
          /**
               * 根據地址返回地區
               * 
          @param address
               * 
          @return
               
          */
              def processWorkTwo(String address){
              }
              
          }

          用戶工作服務:
          package com.test.services

          import com.test.domian.User

          class UserService {

              def workService
              
              def doWork() {
                  
                  def userList 
          = User.list()
                  userList.each {
                      it.name 
          = workService.processWorkOne(it.name)
                      it.address 
          = workService.processWorkTwo(it.address)
                      
                  }
              }
          }

          我們重點來看下測試類:
          package com.test.services

          import grails.test.*

          import com.test.domian.User

          class UserServiceTests extends GrailsUnitTestCase {
              
          protected void setUp() {
                  
          super.setUp()
              }

              
          protected void tearDown() {
                  
          super.tearDown()
              }

              
          void testDoWork() {
                  
                  
          //構造數據,類似于數據庫存在三條記錄
                  def user1 = new User(id:1, name:"lucy", address:"hangzhou")
                  def user2 
          = new User(id:2, name:"lily", address:"wenzhou")
                  def user3 
          = new User(id:3, name:"lilei", address:"beijing")
                  mockDomain User, [user1, user2, user3]
                  
                  
          //mock WorkService接口的processWorkOne方法和processWorkTwo方法
                  def workControl = mockFor(WorkService)
                  def userCount 
          = User.count()
                  
          while(userCount-- > 0){
                      workControl.demand.processWorkOne(
          1..1){String userName ->
                          
          return "hello world, " << userName
                      }
                      workControl.demand.processWorkTwo(
          1..1){String address ->
                          
          return "location in " << address
                      }
                  }
                  def workService 
          = workControl.createMock()
                  
                  
          //把構造好的workservice傳給userservice
                  UserService userService = new UserService()
                  userService.workService 
          = workService
                  
                  userService.doWork()
                  
                  def user4 
          = User.findById(1)
                  assertEquals 
          "hello world, lucy", user4.name
                  assertEquals 
          "location in hangzhou", user4.address
              }
          }

          以下著重來具體說明:
          1、mockDomain方法就是構造數據,包括domain類的動態方法都可以使用,比如:save(),list(),findby*()等。代碼中的User.count(); User.list();就是因為調用了mockDomain方法才可以正常使用。如果是集成測試的話,grails會幫我們構造好,可以直接使用。但這里是單元測試,所以需要自己mock。

          2、mockFor方法就是給WorkService構造一個對象,然后給
          workControl對象的demand代理創建兩個UserService中用的processWorkOne和processWorkTwo方法,代碼中用到了1..1,表示mock對象只能調用這個方法一次,為什么要循環三次設置processWorkOne和processWorkTwo方法呢?因為我們在UserService是對三個對象分別進行調用處理這兩件事情。也許你會想,干嘛不直接把1..3(最少調用一次,最多調用三次)。是的,我最開始也是這么來處理,可是單元測試就是同不過。
          如果把
          UserService類中的
          workControl.demand.processWorkOne(1..1){String userName ->
                
          return "hello world, " << userName
           }

          改成
          workControl.demand.processWorkOne(1..3){String userName ->
                
          return "hello world, " << userName
           }

          然后把UserServiceTests類中的:
          userList.each {
                      it.name 
          = workService.processWorkOne(it.name)
                      it.address 
          = workService.processWorkTwo(it.address)
          }
          改成
          userList.each {
                      it.name 
          = workService.processWorkOne(it.name)
                     
          it.name = workService.processWorkOne(it.name)
                      it.name = workService.processWorkOne(it.name)
                      it.address = workService.processWorkTwo(it.address)
          }

          單元測試可以通過,但是改成這樣
          userList.each {
                      it.name 
          = workService.processWorkOne(it.name)
                     
          it.name = workService.processWorkOne(it.name)
                      it.address = workService.processWorkTwo(it.address)
                     
          it.name = workService.processWorkOne(it.name)
          }
          單元測試通不過。
          以上就是表明1..3的含義:這個方法要連續被調用至少一次,至多三次。
          但是有的人說我在UserService中就要這么寫
          userList.each {
                      it.name 
          = workService.processWorkOne(it.name)
                     
          it.name = workService.processWorkOne(it.name)
                      it.address = workService.processWorkTwo(it.address)
                     
          it.name = workService.processWorkOne(it.name)
          }
          那我要怎么改單元測試才能通過?
          我們把
          UserServiceTests的demand這段代碼
          workControl.demand.processWorkOne(1..1){String userName ->
               
          return "hello world, " << userName
          }
          workControl.demand.processWorkTwo(
          1..1){String address ->
                
          return "location in " << address
          }

          改成
          workControl.demand.processWorkOne(1..2){String userName ->
                
          return "hello world, " << userName
          }
          workControl.demand.processWorkTwo(
          1..1){String address ->
                 
          return "location in " << address
           }

          workControl.demand.processWorkOne(1..1){String address ->
                 
          return "location in " << address
           }
          這樣就通過了。
          以上就是說明構造出來的函數只能按照構造的順序調用。今天就是因為這個花了我好長時間啊,希望我理解是正確的。如有不對,請留言糾正。



          posted on 2011-05-13 21:40 yangpingyu 閱讀(1888) 評論(0)  編輯  收藏 所屬分類: grails


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          <2011年5月>
          24252627282930
          1234567
          891011121314
          15161718192021
          22232425262728
          2930311234

          導航

          統計

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          收藏夾

          linux

          產品交互

          分析,設計,架構

          安全

          技術牛人

          數據庫

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 中卫市| 太白县| 开化县| 阳山县| 原阳县| 莲花县| 喀喇| 通山县| 方正县| 西林县| 临潭县| 阳原县| 漳平市| 古浪县| 明水县| 凤阳县| 页游| 佛教| 措美县| 班戈县| 东方市| 策勒县| 南江县| 常熟市| 客服| 洞口县| 永清县| 孟连| 蒙阴县| 阿拉善盟| 乌拉特中旗| 潞城市| 志丹县| 永福县| 灵寿县| 保定市| 项城市| 凤山市| 游戏| 五寨县| 济南市|