少年阿賓

          那些青春的歲月

            BlogJava :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
            500 Posts :: 0 Stories :: 135 Comments :: 0 Trackbacks

          #

          //UserDao.java

          package com.abin.lee.jmock;

          /**
           * Created with IntelliJ IDEA.
           * User: abin
           * Date: 13-4-23
           * Time: 上午11:07
           * To change this template use File | Settings | File Templates.
           */
          public interface UserDao {
              public String getMessage(String message);
          }





          //UserService.java

          package com.abin.lee.jmock;

          /**
           * Created with IntelliJ IDEA.
           * User: abin
           * Date: 13-4-23
           * Time: 上午11:06
           * To change this template use File | Settings | File Templates.
           */
          public class UserService {
             private UserDao userDao;

             public String findMessage(String message){
                 return this.userDao.getMessage(message);
             }

              public void setUserDao(UserDao userDao){
                  this.userDao=userDao;
              }
          }





          //UserManageTest.java

          package com.abin.lee.jmock;

          import junit.framework.TestCase;
          import org.jmock.Expectations;
          import org.jmock.Mockery;
          import org.junit.Test;

          /**
           * Created with IntelliJ IDEA.
           * User: abin
           * Date: 13-4-23
           * Time: 上午11:08
           * To change this template use File | Settings | File Templates.
           */
          public class UserManageTest {
              @Test
              public void testUserManage(){
                  // 建立一個(gè)test上下文對(duì)象。
                  Mockery mockery=new Mockery();
                  // 生成一個(gè)mock對(duì)象
                  final UserDao userDao=mockery.mock(UserDao.class);
                  // 設(shè)置期望。
                  mockery.checking(new Expectations(){
                          {
                              // 當(dāng)參數(shù)為"abin"的時(shí)候,userDao對(duì)象的getMessage方法被調(diào)用一次,并且返回西安。
                              oneOf(userDao).getMessage("abin");
                              will(returnValue("abin"));
                          }
                  });
                  UserService userService=new UserService();
                  userService.setUserDao(userDao);
                  String message=userService.findMessage("abin");
                  System.out.println("message="+message);
                  TestCase.assertEquals("abin",message);

              }
          }

          posted @ 2013-04-23 11:29 abin 閱讀(582) | 評(píng)論 (0)編輯 收藏

          手頭的項(xiàng)目越來越大,很多以前不會(huì)出現(xiàn)的問題開始浮現(xiàn)。

           

          比如:我修改了一個(gè)基礎(chǔ)的類庫,卻意外的影響了九重天外的客戶項(xiàng)目,直接導(dǎo)致一個(gè)功能無法實(shí)現(xiàn)。我郁悶啊!!!

           

          因此開始要有組織、有預(yù)謀、有計(jì)劃的對(duì)項(xiàng)目過程進(jìn)行測(cè)試驅(qū)動(dòng)了。最終目標(biāo)是,我修改了底層某個(gè)dll的某個(gè)方法,測(cè)試框架能夠自動(dòng)幫我找出來所有收到影響的類,全部執(zhí)行一次回歸測(cè)試,并發(fā)送一份漂亮的報(bào)告到我手里。

           

          這個(gè)目標(biāo)估計(jì)1、2個(gè)星期才能實(shí)現(xiàn),不過現(xiàn)在先放出一個(gè)非常漂亮的MOCK核心代碼。 

           

          研究過程

          在不斷收集各種資料過程中,學(xué)習(xí)了很多,例如以下關(guān)鍵字,有興趣的兄弟可以自己搜索一下:

           

          testdriven.net, nunit,  typemock, cruiseControl.net, Confluence, JIRE, NUnitForms, WatiN, MBUnit, CSUnit, NBehave, Gallio

          ranorex,  dynamicProxy...

           

          估計(jì)各位有時(shí)間看看上面的簡(jiǎn)介,就能夠掌握現(xiàn)在測(cè)試驅(qū)動(dòng)的大致發(fā)展。

           

          1. nunit的核心代碼非常容易理解,大伙自己下載看看就行了。
          2. testdriven.net 的前身是:NUnitAddin, 如果要研究如何testdriven集成到vs,看看不錯(cuò)。
          3. WatiN的核心代碼雖然我沒有看,不過猜也能猜到,就是調(diào)用了IE的核,然后搜索上面的html標(biāo)簽操作。
          4. typeMock有點(diǎn)混蛋,源碼混淆了,無法拿到核心技術(shù),不過從介紹來看是源自了castle.DynamicProxy,那么各位可以參觀一下一篇非常垃圾的文章,但是起碼讓我入門了: http://www.cublog.cn/u/23701/showart_1414436.html
          5. 最后,我來到了Moq,開始因?yàn)槁犝f是.net3.5,就沒有看源碼,不過剛才研究了一下頓時(shí)非常興奮。起碼Moq能讓我解決了50%的工作。

           

          接下來就說下Mock技術(shù)和測(cè)試驅(qū)動(dòng)中的作用。

           

          Mock技術(shù) 

          我最不喜歡老外造名詞,所以會(huì)用自己的體會(huì)去介紹。

          mock的本質(zhì)就是模擬目標(biāo)對(duì)象的一個(gè)假對(duì)象。 

          這個(gè)性質(zhì)在測(cè)試驅(qū)動(dòng)中非常有用,例如我的代碼是:

          代碼
                  public DateTime GetNextFiredDate(DateTime now, IOrmScheduleTrigger trigger, int triggeredtimes)
                  {
                      
          return GetNextFiredDate(now, trigger.TriggerType, trigger.TriggerExpression, triggeredtimes);
                  }

           

           

           現(xiàn)在要測(cè)試這個(gè)代碼,就需要傳入一個(gè)IOrmScheduleTrrigger的接口對(duì)象。但是不幸的是,這個(gè)對(duì)象是個(gè)ORM,要啟動(dòng)這個(gè)對(duì)象,就涉及到了數(shù)據(jù)庫。。。。

           

          老大,我只是想測(cè)試一下一輛寶馬的玻璃是否堅(jiān)硬,不需要啟動(dòng)我的寶馬加速到120km,然后再用手去翹翹那塊玻璃吧。

           

          所以,我希望能夠有個(gè)模擬對(duì)象,繼承了這個(gè)接口, 同時(shí)提供了我期望的返回值,讓這個(gè)方法能夠順利執(zhí)行。

           

          傳統(tǒng)的傻逼方法,就是自己寫一個(gè)類,繼承了這個(gè)接口,然后才傳入進(jìn)去。例如:

           

          public class OrmScheduleTriggerTestObject : IOrmScheduleTrigger
          {
          // some method here
          }

           

           

          這樣不就更加的傻逼了?我為了測(cè)一塊玻璃,還親自造了另外一臺(tái)簡(jiǎn)易的寶馬出來? 于是我開始翻閱各種文獻(xiàn),甚至考慮使用動(dòng)態(tài)代理(DynamicProxy)。動(dòng)態(tài)代理的核心思想就是在代碼運(yùn)行中寫IL生成一個(gè)繼承類。這個(gè)技術(shù)很有用,但是現(xiàn)在我還用不上(就像martin fowler說的,typemock就等于把核武器交給了一個(gè)4歲小孩)。

           

          于是我繼續(xù)尋找,終于翻開了Moq的源碼,找到了答案。

           

          先看看以下一段代碼,是我摘自Moq源碼的核心部分,稍加改造了:

           

          復(fù)制代碼
          代碼
          using System;
          using System.Collections.Generic;
          using System.Text;
          using System.Runtime.Remoting.Proxies;
          using System.Runtime.Remoting.Messaging;

          namespace Pixysoft.Framework.TestDrivens
          {
              
          public class Mock<TInterface> : RealProxy
              {
                  
          public Mock()
                      : 
          base(typeof(TInterface))
                  {
                  }

                  
          public TInterface Value
                  {
                      
          get
                      {
                          
          return (TInterface)this.GetTransparentProxy();
                      }
                  }

                  
          public override IMessage Invoke(IMessage msg)
                  {
                      IMethodCallMessage methodCall 
          = msg as IMethodCallMessage;

                      
          //我返回int = 1

                      
          return new ReturnMessage(1null0null, methodCall);
                  }
              }

              
          public interface IMock
              {
                  
          int Devide(int a, int b);
              }

              
          public class testrealproxy //測(cè)試代碼在這里!!!
              {
                  
          public void test()
                  {
                      IMock mock 
          = new Mock<IMock>().Value;

                      Console.WriteLine(mock.Devide(
          12));

                      
          //輸出 = 1
                  }
              }
          }
          復(fù)制代碼

           

           

          這段代碼就是Moq的核心思想。

           

          大概意思是:我希望調(diào)用接口IMock的方法Devide,但是我壓根不想寫這個(gè)接口的實(shí)現(xiàn)。

           

          那么我先寫一個(gè)通用的模擬對(duì)象Mock<TInterface>,繼承了RealProxy。

           

          然后通過調(diào)用Value就可以返回需要的接口對(duì)象。而這個(gè)對(duì)象就是 return (TInterface)this.GetTransparentProxy();是個(gè)透明代理。

           

          最后當(dāng)我調(diào)用了 int Devide(int a, int b); 方法的時(shí)候,等于調(diào)用了public override IMessage Invoke(IMessage msg)方法(有點(diǎn)點(diǎn)的AOP感覺)。

           

          后記

           

          上文就是Moq的核心思想了。非常的精彩!估計(jì)有了思路,各位就可以制造自己的原子彈了。

           

          這里插句題外話,很多人抨擊重復(fù)造輪子。我就奇怪了。如果我造一個(gè)輪子花費(fèi)的時(shí)間和學(xué)習(xí)用一個(gè)輪子的時(shí)間差不遠(yuǎn),為什么不造一個(gè)?

          而且,用別人的輪子,經(jīng)常出現(xiàn)的情況是:很多輪子不知道挑哪個(gè)。一旦挑上了,項(xiàng)目進(jìn)展到一般才發(fā)現(xiàn)不適合、有bug,于是又重頭挑另外的輪子。

           

          這個(gè)經(jīng)歷是真實(shí)的。當(dāng)年讀大學(xué),我的室友就是典型的挑輪子,他懂得很多框架(java),webwork,hibernate, spring。和人砍起來朗朗上口,但是需要深入做項(xiàng)目了,出現(xiàn)問題基本上不知所措,不是翻文獻(xiàn),就是問師兄,最后整個(gè)項(xiàng)目組從來就沒有一個(gè)成品。

           

          我自從學(xué)電腦依賴,從來就沒有用過別人的輪子,即使是hibernate,我的確也沒有用過,不過他的核心文檔倒是看過,對(duì)比之下,和oracle的toplink相比簡(jiǎn)直就是小孩。

           

          比我牛逼的兄弟大有人在,希望各位牛人不要浪費(fèi)自己的時(shí)間去挑別人的輪子,直接自己造一個(gè)算了。 

           

          最后說說接下來的工作。

           

          基于接口的測(cè)試驅(qū)動(dòng)完成了,剩下的就是面對(duì)sealed class 等頑固分子了, 必然需要?jiǎng)佑梅浅R?guī)武器,DynamicProxy。下回再見。

           

           

          分享到:
          posted @ 2013-04-20 15:29 abin 閱讀(491) | 評(píng)論 (0)編輯 收藏

          1 現(xiàn)有的單元測(cè)試框架
                 單元測(cè)試是保證程序正確性的一種有效的測(cè)試手段,對(duì)于不同的開發(fā)語言,通常都能找到相應(yīng)的單元框架。



                 借助于這些單測(cè)框架的幫助,能夠使得我們編寫單元測(cè)試用例的過程變得便捷而優(yōu)雅。框架幫我們提供了case的管理,執(zhí)行,斷言集,運(yùn)行參數(shù),全局事件工作,所有的這些使得我們只需關(guān)注:于對(duì)于特定的輸入,被測(cè)對(duì)象的返回是否正常。
                 那么,這些xUnit系列的單元測(cè)試框架是如何做到這些的了?分析這些框架,發(fā)現(xiàn)所有的單元測(cè)試框架都是基于以下的一種體系結(jié)構(gòu)設(shè)計(jì)的。

           
                 如上圖所示,單測(cè)框架中通常包括TestRunner, Test, TestResult, TestCase, TestSuite, TestFixture六個(gè)組件。
          TestRuner:負(fù)責(zé)驅(qū)動(dòng)單元測(cè)試用例的執(zhí)行,匯報(bào)測(cè)試執(zhí)行的結(jié)果,從而簡(jiǎn)化測(cè)試
          TestFixture:以測(cè)試套件的形式提供setUp()和tearDown()方法,保證兩個(gè)test case之間的執(zhí)行是相互獨(dú)立,互不影響的。
          TestResult:這個(gè)組件用于收集每個(gè)test case的執(zhí)行結(jié)果
          Test:作為TestSuite和TestCase的父類暴露run()方法為TestRunner調(diào)用
          TestCase:暴露給用戶的一個(gè)類,用戶通過繼承TestCase,編寫自己的測(cè)試用例邏輯
          TestSuite:提供suite功能管理testCase
                 正因?yàn)橄嗨频捏w系結(jié)構(gòu),所以大多數(shù)單元測(cè)試框架都提供了類似的功能和使用方法。那么在單測(cè)中引入單元測(cè)試框架會(huì)帶來什么好處,在現(xiàn)有單元測(cè)試框架下還會(huì)存在什么樣不能解決的問題呢?
          2 單元測(cè)試框架的優(yōu)點(diǎn)與一些問題
                 在單元測(cè)試中引入單測(cè)框架使得編寫單測(cè)用例時(shí),不需要再關(guān)注于如何驅(qū)動(dòng)case的執(zhí)行,如何收集結(jié)果,如何管理case集,只需要關(guān)注于如何寫好單個(gè)測(cè)試用例即可;同時(shí),在一些測(cè)試框架中通過提供豐富的斷言集,公用方法,以及運(yùn)行參數(shù)使得編寫單個(gè)testcase的過程得到了最大的簡(jiǎn)化。
                 那這其中會(huì)存在什么樣的疑問了?
                 我在單元測(cè)試框架中寫一個(gè)TestCase,與我單獨(dú)寫一個(gè)cpp文件在main()方法里寫測(cè)試代碼有什么本質(zhì)卻別嗎?用了單元測(cè)試框架,并沒有解決我在對(duì)復(fù)雜系統(tǒng)做單測(cè)時(shí)遇到的問題。
                 沒錯(cuò),對(duì)于單個(gè)case這兩者從本質(zhì)上說是沒有區(qū)別的。單元測(cè)試框架本身并沒有告訴你如何去寫TestCase,在這一點(diǎn)上他是沒有提供任何幫助的。所以對(duì)于一些復(fù)雜的場(chǎng)景,只用單元測(cè)試框架是有點(diǎn)多少顯得無能為力的。
                 使用單元測(cè)試框架往往適用于以下場(chǎng)景的測(cè)試:?jiǎn)蝹€(gè)函數(shù),一個(gè)class,或者幾個(gè)功能相關(guān)class的測(cè)試,對(duì)于純函數(shù)測(cè)試,接口級(jí)別的測(cè)試尤其適用,如房貸計(jì)算器公式的測(cè)試。
                 但是,對(duì)于一些復(fù)雜場(chǎng)景:
          ? 被測(cè)對(duì)象依賴復(fù)雜,甚至無法簡(jiǎn)單new出這個(gè)對(duì)象
          ? 對(duì)于一些failure場(chǎng)景的測(cè)試
          ? 被測(cè)對(duì)象中涉及多線程合作
          ? 被測(cè)對(duì)象通過消息與外界交互的場(chǎng)景
          ? …
                 單純依賴單測(cè)框架是無法實(shí)現(xiàn)單元測(cè)試的,而從某種意義上來說,這些場(chǎng)景反而是測(cè)試中的重點(diǎn)。
                 以分布式系統(tǒng)的測(cè)試為例,class 與 function級(jí)別的單元測(cè)試對(duì)整個(gè)系統(tǒng)的幫助不大,當(dāng)然,這種單元測(cè)試對(duì)單個(gè)程序的質(zhì)量有幫助;分布式系統(tǒng)測(cè)試的要點(diǎn)是測(cè)試進(jìn)程間的交互:一個(gè)進(jìn)程收到客戶請(qǐng)求,該如何處理,然后轉(zhuǎn)發(fā)給其他進(jìn)程;收到響應(yīng)之后,又修改并應(yīng)答客戶;同時(shí)分布式系統(tǒng)測(cè)試中通常更關(guān)注一些異常路徑的測(cè)試,這些場(chǎng)景才是測(cè)試中的重點(diǎn),也是難點(diǎn)所在。
                 Mock方法的引入通常能幫助我們解決以上場(chǎng)景中遇到的難題。
          3 Mock的引入帶來了什么
                 在維基百科上這樣描述Mock:In object-oriented programming, mock objects are simulated objects that mimic the behavior of real objects in controlled ways. A computer programmer typically creates a mock object to test the behavior of some other object, in much the same way that a car designer uses a crash test dummy to simulate the dynamic behavior. of a human in vehicle impacts.
                 Mock通常是指,在測(cè)試一個(gè)對(duì)象A時(shí),我們構(gòu)造一些假的對(duì)象來模擬與A之間的交互,而這些Mock對(duì)象的行為是我們事先設(shè)定且符合預(yù)期。通過這些Mock對(duì)象來測(cè)試A在正常邏輯,異常邏輯或壓力情況下工作是否正常。
                 引入Mock最大的優(yōu)勢(shì)在于:Mock的行為固定,它確保當(dāng)你訪問該Mock的某個(gè)方法時(shí)總是能夠獲得一個(gè)沒有任何邏輯的直接就返回的預(yù)期結(jié)果。
                 Mock Object的使用通常會(huì)帶來以下一些好處:
          ? 隔絕其他模塊出錯(cuò)引起本模塊的測(cè)試錯(cuò)誤。
          ? 隔絕其他模塊的開發(fā)狀態(tài),只要定義好接口,不用管他們開發(fā)有沒有完成。
          ? 一些速度較慢的操作,可以用Mock Object代替,快速返回。
                 對(duì)于分布式系統(tǒng)的測(cè)試,使用Mock Object會(huì)有另外兩項(xiàng)很重要的收益:
          ? 通過Mock Object可以將一些分布式測(cè)試轉(zhuǎn)化為本地的測(cè)試
          ? 將Mock用于壓力測(cè)試,可以解決測(cè)試集群無法模擬線上集群大規(guī)模下的壓力
          4 Mock的應(yīng)用場(chǎng)景
                 在使用Mock的過程中,發(fā)現(xiàn)Mock是有一些通用性的,對(duì)于一些應(yīng)用場(chǎng)景,是非常適合使用Mock的:
          ? 真實(shí)對(duì)象具有不可確定的行為(產(chǎn)生不可預(yù)測(cè)的結(jié)果,如股票的行情)
          ? 真實(shí)對(duì)象很難被創(chuàng)建(比如具體的web容器)
          ? 真實(shí)對(duì)象的某些行為很難觸發(fā)(比如網(wǎng)絡(luò)錯(cuò)誤)
          ? 真實(shí)情況令程序的運(yùn)行速度很慢
          ? 真實(shí)對(duì)象有用戶界面
          ? 測(cè)試需要詢問真實(shí)對(duì)象它是如何被調(diào)用的(比如測(cè)試可能需要驗(yàn)證某個(gè)回調(diào)函數(shù)是否被調(diào)用了)
          ? 真實(shí)對(duì)象實(shí)際上并不存在(當(dāng)需要和其他開發(fā)小組,或者新的硬件系統(tǒng)打交道的時(shí)候,這是一個(gè)普遍的問題)
                 當(dāng)然,也有一些不得不Mock的場(chǎng)景:
          一些比較難構(gòu)造的Object:這類Object通常有很多依賴,在單元測(cè)試中構(gòu)造出這樣類通常花費(fèi)的成本太大。
          ? 執(zhí)行操作的時(shí)間較長(zhǎng)Object:有一些Object的操作費(fèi)時(shí),而被測(cè)對(duì)象依賴于這一個(gè)操作的執(zhí)行結(jié)果,例如大文件寫操作,數(shù)據(jù)的更新等等,出于測(cè)試的需求,通常將這類操作進(jìn)行Mock。
          ? 異常邏輯:一些異常的邏輯往往在正常測(cè)試中是很難觸發(fā)的,通過Mock可以人為的控制觸發(fā)異常邏輯。
                  在一些壓力測(cè)試的場(chǎng)景下,也不得不使用Mock,例如在分布式系統(tǒng)測(cè)試中,通常需要測(cè)試一些單點(diǎn)(如namenode,jobtracker)在壓力場(chǎng)景下的工作是否正常。而通常測(cè)試集群在正常邏輯下無法提供足夠的壓力(主要原因是受限于機(jī)器數(shù)量),這時(shí)候就需要應(yīng)用Mock去滿足。
                  在這些場(chǎng)景下,我們應(yīng)該如何去做Mock的工作了,一些現(xiàn)有的Mock工具可以幫助我們進(jìn)行Mock工作。
          5 Mock工具的介紹
                 手動(dòng)的構(gòu)造 Mock 對(duì)象通常帶來額外的編碼量,而且這些為創(chuàng)建 Mock 對(duì)象而編寫的代碼很有可能引入錯(cuò)誤。目前,有許多開源項(xiàng)目對(duì)動(dòng)態(tài)構(gòu)建 Mock 對(duì)象提供了支持,這些項(xiàng)目能夠根據(jù)現(xiàn)有的接口或類動(dòng)態(tài)生成,這樣不僅能避免額外的編碼工作,同時(shí)也降低了引入錯(cuò)誤的可能。
          C++:   GoogleMock   http://code.google.com/p/googlemock/

          Java:   EasyMock   http://easymock.org/

                 通常Mock工具通過簡(jiǎn)單的方法對(duì)于給定的接口生成 Mock 對(duì)象的類庫。它提供對(duì)接口的模擬,能夠通過錄制、回放、檢查三步來完成大體的測(cè)試過程,可以驗(yàn)證方法的調(diào)用種類、次數(shù)、順序,可以令 Mock 對(duì)象返回指定的值或拋出指定異常。通過這些Mock工具我們可以方便的構(gòu)造 Mock 對(duì)象從而使單元測(cè)試順利進(jìn)行,能夠應(yīng)用于更加復(fù)雜的測(cè)試場(chǎng)景。
                 以EasyMock為例,通過 EasyMock,我們可以為指定的接口動(dòng)態(tài)的創(chuàng)建 Mock 對(duì)象,并利用 Mock 對(duì)象來模擬協(xié)同模塊,從而使單元測(cè)試順利進(jìn)行。這個(gè)過程大致可以劃分為以下幾個(gè)步驟:
          使用 EasyMock 生成 Mock 對(duì)象
          ? 設(shè)定 Mock 對(duì)象的預(yù)期行為和輸出 
          ? 將 Mock 對(duì)象切換到 Replay 狀態(tài)
          ? 調(diào)用 Mock 對(duì)象方法進(jìn)行單元測(cè)試
          ? 對(duì) Mock 對(duì)象的行為進(jìn)行驗(yàn)證
          EasyMock的使用和原理:  http://www.ibm.com/developerworks/cn/opensource/os-cn-easymock/

                 EasyMock 后臺(tái)處理的主要原理是利用 java.lang.reflect.Proxy 為指定的接口創(chuàng)建一個(gè)動(dòng)態(tài)代理,這個(gè)動(dòng)態(tài)代理,就是我們?cè)诰幋a中用到的 Mock 對(duì)象。EasyMock 還為這個(gè)動(dòng)態(tài)代理提供了一個(gè) InvocationHandler 接口的實(shí)現(xiàn),這個(gè)實(shí)現(xiàn)類的主要功能就是將動(dòng)態(tài)代理的預(yù)期行為記錄在某個(gè)映射表中和在實(shí)際調(diào)用時(shí)從這個(gè)映射表中取出預(yù)期輸出。
                 借助類似于EasyMock這樣工具,大大降低了編寫Mock對(duì)象的成本,通常來說Mock工具依賴于單元測(cè)試框架,為用戶編寫TestCase提供便利,但是本身依賴于單元測(cè)試框架去驅(qū)動(dòng),管理case,以及收集測(cè)試結(jié)果。例如EasyMock依賴于JUint,GoogleMock依賴于Gtest。
                 那么有了單元測(cè)試框架和相應(yīng)的Mock工具就萬事俱備了,還有什么樣的問題?正如單元測(cè)試框架沒有告訴你如何寫TestCase一樣,Mock工具也沒有告訴你如何去選擇Mock的點(diǎn)。
          6 如何選擇恰當(dāng)?shù)膍ock點(diǎn)
                 對(duì)于Mock這里存在兩個(gè)誤區(qū),1.是Mock的對(duì)象越多越好;2.Mock會(huì)引入巨大的工作量,通常得不償失。這都是源于不恰當(dāng)?shù)腗ock點(diǎn)的選取。
                 這里說的如何選擇恰當(dāng)?shù)膍ock點(diǎn),是說對(duì)于一個(gè)被測(cè)對(duì)象,我們應(yīng)當(dāng)在外圍選擇恰當(dāng)?shù)膍ock對(duì)象,以及需要mock的接口。因?yàn)閷?duì)于任意一個(gè)對(duì)象,任意一段代碼邏輯我們都是有辦法進(jìn)行Mock的,而Mock點(diǎn)選擇直接決定了我們Mock的工作量以及測(cè)試效果。從另外一種意義上來說,不恰當(dāng)Mock選擇反而會(huì)對(duì)我們的測(cè)試產(chǎn)生誤導(dǎo),從而在后期的集成和系統(tǒng)測(cè)試中引入更多的問題。
                 在mock點(diǎn)的選擇過程中,以下的一些點(diǎn)會(huì)是一些不錯(cuò)的選擇
          ? 網(wǎng)絡(luò)交互:如果兩個(gè)被測(cè)模塊之間是通過網(wǎng)絡(luò)進(jìn)行交互的,那么對(duì)于網(wǎng)絡(luò)交互進(jìn)行Mock通常是比較合適的,如RPC
          ? 外部資源:比如文件系統(tǒng)、數(shù)據(jù)源,如果被測(cè)對(duì)象對(duì)此類外部資源依賴性非常強(qiáng),而其行為的不可預(yù)測(cè)性很可能導(dǎo)致測(cè)試的隨機(jī)失敗,此類的外部資源也適合進(jìn)行Mock。
          ? UI:因?yàn)閁I很多時(shí)候都是用戶行為觸發(fā)事件,系統(tǒng)本身只是對(duì)這些觸發(fā)事件進(jìn)行相應(yīng),對(duì)這類UI做Mock,往往能夠?qū)崿F(xiàn)很好的收益,很多基于關(guān)鍵字驅(qū)動(dòng)的框架都是基于UI進(jìn)行Mock的
          ? 第三方API:當(dāng)接口屬于使用者,通過Mock該接口來確定測(cè)試使用者與接口的交互。
                 當(dāng)然如何做Mock一定是與被系統(tǒng)的特性精密關(guān)聯(lián)的,一些強(qiáng)制性的約束和規(guī)范是不合適的。這里介紹幾個(gè)做的比較好的mock的例子。
                 1. 殺毒軟件更新部署模塊的Mock
                 這個(gè)例子源于一款殺毒產(chǎn)品的更新部署模塊的測(cè)試。對(duì)于一個(gè)殺毒軟件客戶端而言,需要通過更新檢查模塊與病毒庫Server進(jìn)行交互,如果發(fā)現(xiàn)病毒庫有更新則觸發(fā)病毒庫部署模塊的最新病毒庫的數(shù)據(jù)請(qǐng)求和部署工作,要求部署完成后殺毒軟件客戶端能夠正常工作。
           

                  對(duì)于這一場(chǎng)景的測(cè)試,當(dāng)時(shí)受限于這樣一個(gè)條件,通常的病毒庫server通常最多一天只更新一次病毒庫,也就是說如果使用真實(shí)的病毒庫server,那么針對(duì)更新部署模塊的測(cè)試一天只能被觸發(fā)一次。這是測(cè)試中所不能容忍的,通過對(duì)病毒庫server進(jìn)行mock可以解決這個(gè)問題。
                 對(duì)于這個(gè)場(chǎng)景可以采取這樣一種Mock方式:用一個(gè)本地文件夾來模擬病毒庫server,選擇更新部署模塊與病毒庫server之間交互的兩個(gè)函數(shù)checkVersion(),reqData()函數(shù)進(jìn)行Mock。
                 checkVersion()工作原先的工作是檢查病毒庫Server的版本號(hào),以決定是否觸發(fā)更新,將其行為Mock為檢查一個(gè)本地文件夾中病毒庫的版本號(hào);reqData()原有的行為是從病毒庫Server拖取病毒庫文件,將其Mock為從本地文件夾中拖取病毒庫文件。通過這種方式我們用一個(gè)本地文件夾Mock病毒庫Server的行為,其帶來的產(chǎn)出是:我們可以隨意的觸發(fā)病毒庫更新操作以及各種異常。通過這種方式發(fā)現(xiàn)了一個(gè)在更新部署過程中,病毒庫Server的病毒庫版本發(fā)生改變?cè)斐沙鲥e(cuò)的嚴(yán)重bug,這個(gè)是在原有一天才觸發(fā)一次更新操作的情況下永遠(yuǎn)也無法發(fā)現(xiàn)的。
                 2. 分布式系統(tǒng)中對(duì)NameNode模塊的測(cè)試
           

                 在測(cè)試NameNode模塊的過程中存在這樣一個(gè)問題,在正常邏輯無壓力條件下NameNode模塊都是工作正常的。但是線上集群在大壓力的情況下,是有可能觸發(fā)NameNode的問題的。但是原有的測(cè)試方法下,我們是無法對(duì)NameNode模擬大壓力的場(chǎng)景的(因?yàn)镹ameNode的壓力主要來源于DateNode數(shù)量,而我們測(cè)試集群是遠(yuǎn)遠(yuǎn)無法達(dá)到線上幾千臺(tái)機(jī)器的規(guī)模的),而NameNode單點(diǎn)的性能瓶頸問題恰恰是測(cè)試的重點(diǎn),真實(shí)的DataNode是無法滿足測(cè)試需求的,我們必須對(duì)DataNode進(jìn)行Mock。
           

                 如何對(duì)DateNode進(jìn)行Mock了,最直觀的想法是選擇NameNode與DataNode之間的交互接口進(jìn)行Mock,也就是他們之間的RPC交互,但是由于NameNode與DataNode之間的交互信息種類很多,所以其實(shí)這并不是一種很好的選擇。
                 換個(gè)角度來想,NameNode之上的壓力是源于對(duì)HDFS的讀寫操作造成的NameNode上元數(shù)據(jù)的維護(hù),也就是說,對(duì)于NameNode而言,其實(shí)他并不關(guān)心數(shù)據(jù)到底寫到哪里去了,只關(guān)心數(shù)據(jù)是否讀寫成功。如果是這種場(chǎng)景Mock就可以變的簡(jiǎn)單了,我們可以直接將DataNode上對(duì)塊的操作進(jìn)行mock,比如,對(duì)一次寫請(qǐng)求,DataNode并不觸發(fā)真實(shí)的寫操作,而直接返回成功。通過這種方式,DataNode去除了執(zhí)行功能,只保留了消息交互功能,間接的實(shí)現(xiàn)了我們的測(cè)試需求,且工作量比之第一種方案小很多。
                 3. 開源社區(qū)提供的MRUnit測(cè)試框架
                 在原有框架下,對(duì)于MapReduce程序的測(cè)試通常是無法在本地驗(yàn)證的,更不用說對(duì)MapReduce程序進(jìn)行單測(cè)了。而MRUnit通過一個(gè)簡(jiǎn)單而優(yōu)雅的Mock,卻實(shí)現(xiàn)了一個(gè)基于MapReduce程序的單測(cè)框架。

          基于MRUINT框架可以將單測(cè)寫成如下形式:

           

                 在這個(gè)框架中定義了MapDriver,ReducerDriver,MapReduceDriver三個(gè)有點(diǎn)類似容器的driver,通過driver來驅(qū)動(dòng)map,reduce或者整個(gè)mapreduce過程的執(zhí)行。
                 如上例,在driver中設(shè)定mapper為IdentityMapper,通過withInput方法設(shè)定輸入數(shù)據(jù),通過withOutput方法設(shè)定預(yù)期結(jié)果,通過runTest方法來觸發(fā)執(zhí)行并進(jìn)行結(jié)果檢測(cè)
                 他的實(shí)現(xiàn)原理是將outputCollector做Mock,outputCollectort中的emit方法實(shí)現(xiàn)的邏輯是將數(shù)據(jù)寫到文件系統(tǒng)中,Mock后是通過另外一個(gè)進(jìn)程去收集數(shù)據(jù)并保存在內(nèi)存中,從而實(shí)現(xiàn)最終結(jié)果的可檢驗(yàn)(在自己的數(shù)據(jù)結(jié)構(gòu)中比對(duì)結(jié)果)。
                 實(shí)現(xiàn)的原理很簡(jiǎn)單,這樣做mock就會(huì)精巧,只選擇最底層的一些簡(jiǎn)單卻又依賴廣泛的點(diǎn)(依賴廣泛指模塊間的數(shù)據(jù)流通常都走這樣的點(diǎn)過)做mock,這樣通常效果很好且簡(jiǎn)單
                 當(dāng)然這個(gè)例子中也有一些缺陷:1.因?yàn)樵趏utputcollector層做mock的數(shù)據(jù)截取,使得無法過partition的分桶邏輯;2.這個(gè)框架是寫內(nèi)存的,無法最終改成壓力性能測(cè)試工具。

          7 附錄
          1. EasyMock示例:
           

          posted @ 2013-04-20 15:21 abin 閱讀(414) | 評(píng)論 (0)編輯 收藏

          1、什么情況下會(huì)使用mock技術(shù)

            (1)需要將當(dāng)前被測(cè)單元和其依賴模塊獨(dú)立開來,構(gòu)造一個(gè)獨(dú)立的測(cè)試環(huán)境,不關(guān)注被測(cè)單元的依賴對(duì)象,只關(guān)注被測(cè)單元的功能邏輯

            ----------比如被測(cè)代碼中需要依賴第三方接口返回值進(jìn)行邏輯處理,可能因?yàn)榫W(wǎng)絡(luò)或者其他環(huán)境因素,調(diào)用第三方經(jīng)常會(huì)中斷或者失敗,無法對(duì)被測(cè)單元進(jìn)行測(cè)試,這個(gè)時(shí)候就可以使用mock技術(shù)來將被測(cè)單元和依賴模塊獨(dú)立開來,使得測(cè)試可以進(jìn)行下去。

            (2)被測(cè)單元依賴的模塊尚未開發(fā)完成,而被測(cè)單元需要依賴模塊的返回值進(jìn)行后續(xù)處理

            ----------比如service層的代碼中,包含對(duì)Dao層的調(diào)用,但是,DAO層代碼尚未實(shí)現(xiàn)

            (3)被測(cè)單元依賴的對(duì)象較難模擬或者構(gòu)造比較復(fù)雜

            ----------比如,支付寶支付的異常條件有很多,但是模擬這種異常條件很復(fù)雜或者無法模擬,比如,查詢聚劃算的訂單結(jié)果,無法在測(cè)試環(huán)境進(jìn)行模擬

            2、Mock技術(shù)分類

            (1)手動(dòng)構(gòu)造mock對(duì)象

            ---------------比如,可以自己寫某個(gè)接口方法的實(shí)現(xiàn),根據(jù)需要編寫返回值,測(cè)試代碼中使用該實(shí)現(xiàn)類對(duì)象

            缺點(diǎn):會(huì)增加代碼量,在寫mock對(duì)象代碼時(shí),有可能引入錯(cuò)誤

            (2)使用開源代碼提供的構(gòu)造mock方法

            --------------比如easyMock,提供了對(duì)接口類的模擬,能夠通過錄制、回放、檢查三步來完成大體的測(cè)試過程,可以驗(yàn)證方法的調(diào)用種類、次數(shù)、順序,可以令Mock對(duì)象返回指定的值或拋出指定異常

            3、EasyMock使用

            (1)引入easyMock

            ------------在maven工程中,通過pom配置依賴關(guān)系

          <dependency>
              <groupId>org.easymock</groupId>
              <artifactId>easymock</artifactId>
              <version>3.0</version>
              <scope>test</scope>
          </dependency>

            ------------在普通java工程中,通過添加外部包的方式

            (2)使用easyMock過程

            1)使用EasyMock生成Mock對(duì)象;
            pingJiaDao = mockControl.createMock(IPingJiaDao.class);

            2)設(shè)定Mock對(duì)象的預(yù)期行為和輸出;
            EasyMock.expect(pingJiaDao.getGoodPingJiaRate(storeId)).andReturn(0.11);

            3)將Mock對(duì)象切換到Replay狀態(tài);
            EasyMock.replay(pingJiaDao);

            4)調(diào)用Mock對(duì)象方法進(jìn)行單元測(cè)試
            storeService.setStoredao(pingJiaDao);
            double rate = storeService.getStoreGoodRate(storeId);

            5)對(duì)Mock對(duì)象的行為進(jìn)行驗(yàn)證。
            EasyMock.verify(pingJiaDao);

            4、其他easyMock功能

            (1)特殊的mock對(duì)象:niceMock
            (2)參數(shù)匹配器
            (3)重置mock對(duì)象
            (4)模擬異常拋出
            (5)設(shè)置調(diào)用次數(shù)

          posted @ 2013-04-20 15:19 abin 閱讀(413) | 評(píng)論 (0)編輯 收藏

            /** 
               * HTTP DELETE方法進(jìn)行刪除操作 
               * @param url 
               * @param map 
               * @return 
               * @throws ClientProtocolException 
               * @throws IOException 
               */  
              public static String remoteDelete(String url, Map<String, String> map) throws ClientProtocolException, IOException{  
                  url = JETSUM_PLATFORM_SERVER+url;  
                  HttpClient httpclient = new DefaultHttpClient();  
                  HttpDelete httpdelete= new HttpDelete();  
                  List<NameValuePair> formparams = setHttpParams(map);  
                  String param = URLEncodedUtils.format(formparams, "UTF-8");  
                  httpdelete.setURI(URI.create(url + "?" + param));  
                  HttpResponse response = httpclient.execute(httpdelete);  
                  String httpEntityContent = GetHttpEntityContent(response);  
                  httpdelete.abort();  
                  return httpEntityContent;     
              }       




             /** 
               * 設(shè)置請(qǐng)求參數(shù) 
               * @param map 
               * @return 
               */  
              private static List<NameValuePair> setHttpParams(Map<String, String> map) {  
                  List<NameValuePair> formparams = new ArrayList<NameValuePair>();  
                  Set<Map.Entry<String, String>> set = map.entrySet();  
                  for (Map.Entry<String, String> entry : set) {  
                      formparams.add(new BasicNameValuePair(entry.getKey(), entry.getValue()));  
                  }  
                  return formparams;  
              }  



           /** 
               * 獲得響應(yīng)HTTP實(shí)體內(nèi)容 
               * @param response 
               * @return 
               * @throws IOException 
               * @throws UnsupportedEncodingException 
               */  
              private static String GetHttpEntityContent(HttpResponse response)  
                      throws IOException, UnsupportedEncodingException {  
                  HttpEntity entity = response.getEntity();  
                  if (entity != null) {  
                      InputStream is = entity.getContent();  
                      BufferedReader br = new BufferedReader(new InputStreamReader(is, "UTF-8"));  
                      String line = br.readLine();  
                      StringBuilder sb = new StringBuilder();  
                      while (line != null) {  
                          sb.append(line + "\n");  
                          line = br.readLine();  
                      }  
                      return sb.toString();  
                  }  
                  return "";  
              }  
          posted @ 2013-04-20 00:54 abin 閱讀(819) | 評(píng)論 (0)編輯 收藏

          怎么配置Tomcat支持HTTP Delete和Put 方法

          如何配置Tomcat支持HTTP Delete和Put 方法
          在tomcat web.xml文件中配置org.apache.catalina.servlets.DefaultServlet的
          <init-param>     
          <param-name>readonly</param-name>
          <param-value>false</param-value>
          </init-param>
          readonly參數(shù)默認(rèn)是true,即不允許delete和put操作,所以通過XMLHttpRequest對(duì)象的put或者delete方法訪問就會(huì)報(bào)告http 403錯(cuò)誤。為REST服務(wù)起見,應(yīng)該設(shè)置該屬性為false。

          posted @ 2013-04-20 00:03 abin 閱讀(1354) | 評(píng)論 (0)編輯 收藏

          邏輯:

          String url = "http://www.baidu.com";

          //將要訪問的url字符串放入HttpPost中

          HttpPost httpPost = new HttpPost(url);

          //請(qǐng)求頭 放置一些修改http請(qǐng)求頭和cookie

          httpPost.setHeader("Accept", "application/json");

          ......

          //如果是HttpPost或者HttpPut請(qǐng)求需要在請(qǐng)求里加參數(shù),而HttpGet或者HttpDelete請(qǐng)求則可以直接拼接到url字符串后面

          //向HttpPost中加入?yún)?shù)

          List<NameValuePair> values = new ArrayList<NameValuePair>();

          values.add(new NameValuePair("id", "1"));

          values.add(new NameValuePair("name", "xiaohong"));

          httpPost.setEntity(new UrlEncodeFormEntity(values, HTTP.UTF_8));  //進(jìn)行轉(zhuǎn)碼

           

          //實(shí)例HttpClient 并執(zhí)行帶有HttpPost的方法,返回HttpResponse 響應(yīng),再進(jìn)行操作

          HttpClient httpClient = new DefaultHttpClient();

          HttpResponse httpResponse = httpClient.execute(httpPost);

          int statusCode = httpResponse.getStatusLine().getStatusCode();  //返回狀態(tài)碼 ,用來進(jìn)行識(shí)別或者判斷訪問結(jié)果

          if(statusCode == 200){

            Instream in = httpResponse.getEntity().getContent();  //要處理該數(shù)據(jù)流是否為GZIP流

          }

           

           

          示例代碼如下:

          package cn.dratek.haoyingsheng.manager.client;

          import cn.dratek.haoyingsheng.manager.util.ResourceUtil;
          import net.dratek.browser.http.Cookie;
          import net.dratek.browser.http.CookieManager;
          import net.dratek.browser.http.URL;
          import org.apache.http.*;
          import org.apache.http.client.entity.UrlEncodedFormEntity;
          import org.apache.http.client.methods.HttpDelete;
          import org.apache.http.client.methods.HttpGet;
          import org.apache.http.client.methods.HttpPost;
          import org.apache.http.client.methods.HttpPut;
          import org.apache.http.impl.client.DefaultHttpClient;
          import org.apache.http.protocol.HTTP;

          import java.io.IOException;
          import java.io.InputStream;
          import java.io.UnsupportedEncodingException;
          import java.util.List;


          public class HttpNetClient {
          /**
          * 所有g(shù)et 請(qǐng)求底層調(diào)用方法
          *
          * @param url 請(qǐng)求url
          * @return byte[] response data
          */
          public static byte[] doGet(String url) {
          InputStream in;
          byte[] bre = null;
          HttpResponse response;
          CookieManager manager = CookieManager.getInstance();
          if (url != null && url.length() != 0) {
          URL myURL = URL.parseString(url);
          Cookie[] cookies = manager.getCookies(myURL);
          HttpGet httpGet = new HttpGet(url);
          if (cookies != null && cookies.length > 0) {
          StringBuilder sb = new StringBuilder();
          for (Cookie ck : cookies) {
          sb.append(ck.name).append('=').append(ck.value).append(";");
          }
          String sck = sb.toString();
          if (sck.length() > 0) {
          httpGet.setHeader("Cookie", sck);
          }

          }
          httpGet.setHeader("Accept-Encoding", "gzip, deflate");
          httpGet.setHeader("Accept-Language", "zh-CN");
          httpGet.setHeader("Accept", "application/json, application/xml, text/html, text/*, image/*, */*");
          try {
          response = new DefaultHttpClient().execute(httpGet);
          if (response != null) {
          int statusCode = response.getStatusLine().getStatusCode();
          if (statusCode == 200 || statusCode == 403) {
          Header[] headers = response.getHeaders("Set-Cookie");
          if (headers != null && headers.length > 0) {
          for (Header header : headers) {
          manager.setCookie(myURL, header.getValue());
          }
          }
          in = response.getEntity().getContent();
          if (in != null) {
          bre = ResourceUtil.readStream(in);
          }

          }
          }


          } catch (IOException e) {
          e.printStackTrace();
          }
          }


          return bre;

          }

          /**
          * 所有Post 請(qǐng)求底層調(diào)用方法
          *
          * @param url 請(qǐng)求url
          * @param values 傳遞的參數(shù)
          * @return byte[] 返回?cái)?shù)據(jù) or null
          */
          public static byte[] doPost(String url, List<NameValuePair> values) {
          System.out.println("url = " + url);
          byte[] bytes = null;
          HttpResponse response;
          InputStream inputStream = null;
          CookieManager manager = CookieManager.getInstance();
          if (url != null && url.length() != 0) {
          URL myurl = URL.parseString(url);
          Cookie[] cookies = manager.getCookies(myurl);
          HttpPost post = new HttpPost(url);
          if (cookies != null && cookies.length > 0) {
          StringBuilder sb = new StringBuilder();
          for (Cookie ck : cookies) {
          sb.append(ck.name).append('=').append(ck.value).append(";");
          }
          String sck = sb.toString();
          if (sck.length() > 0) {
          post.setHeader("Cookie", sck);
          }

          }
          post.setHeader("Accept-Encoding", "gzip, deflate");
          post.setHeader("Accept-Language", "zh-CN");
          post.setHeader("Accept", "application/json, application/xml, text/html, text/*, image/*, */*");
          DefaultHttpClient client = new DefaultHttpClient();
          try {
          if (values != null && values.size() > 0) {
          post.setEntity(new UrlEncodedFormEntity(values, HTTP.UTF_8));
          }
          response = client.execute(post);
          if (response != null) {
          int statusCode = response.getStatusLine().getStatusCode();
          if (statusCode == 200 || statusCode == 403) {
          Header[] headers = response.getHeaders("Set-Cookie");
          if (headers != null && headers.length > 0) {
          for (Header header : headers) {
          manager.setCookie(myurl, header.getValue());
          }
          }
          inputStream = response.getEntity().getContent();
          }
          }

          } catch (UnsupportedEncodingException e) {
          e.printStackTrace();
          } catch (IOException e) {
          e.printStackTrace();
          }
          if (inputStream != null) {
          bytes = ResourceUtil.readStream(inputStream);
          }
          }
          return bytes;
          }

          /**
          * PUT基礎(chǔ)請(qǐng)求
          *
          * @param url 請(qǐng)求地址
          * @param values 提交參數(shù)
          * @return byte[] 請(qǐng)求成功后的結(jié)果
          */
          public static byte[] doPut(String url, List<NameValuePair> values) {
          byte[] ret = null;

          CookieManager manager = CookieManager.getInstance();
          if (url != null && url.length() > 0) {
          URL myUrl = URL.parseString(url);
          StringBuilder sb = new StringBuilder();
          Cookie[] cookies = manager.getCookies(myUrl);
          if (cookies != null && cookies.length > 0) {
          for (Cookie cookie : cookies) {
          sb.append(cookie.name).append("=").append(cookie.value).append(";");
          }

          }
          HttpPut request = new HttpPut(url);
          String sck = sb.toString();
          if (sck.length() > 0) {
          request.setHeader("Cookie", sck);
          }
          request.setHeader("Accept-Encoding", "gzip, deflate");
          request.setHeader("Accept-Language", "zh-CN");
          request.setHeader("Accept", "application/json, application/xml, text/html, text/*, image/*, */*");

          DefaultHttpClient client = new DefaultHttpClient();
          if (values != null && values.size() > 0) {
          try {
          UrlEncodedFormEntity entity;
          entity = new UrlEncodedFormEntity(values);
          request.setEntity(entity);
          } catch (UnsupportedEncodingException e) {
          e.printStackTrace();
          }
          }
          try {
          HttpResponse response = client.execute(request);
          if (response != null) {
          StatusLine statusLine = response.getStatusLine();
          int statusCode = statusLine.getStatusCode();
          if (statusCode == 200 || statusCode == 403) {
          Header[] headers = response.getHeaders("Set-Cookie");
          if (headers != null && headers.length > 0) {
          for (Header header : headers) {
          manager.setCookie(myUrl, header.getValue());
          }
          }
          HttpEntity entity = response.getEntity();
          InputStream inputStream = entity.getContent();
          if (inputStream != null) {
          ret = ResourceUtil.readStream(inputStream);
          inputStream.close();
          }
          }
          }
          } catch (IOException e) {
          e.printStackTrace();
          }
          }
          return ret;
          }

          /**
          * Delete基礎(chǔ)請(qǐng)求
          *
          * @param url 請(qǐng)求地址
          * @return 請(qǐng)求成功后的結(jié)果
          */
          public static byte[] doDelete(String url) {

          InputStream in;
          byte[] bre = null;
          HttpResponse response;
          CookieManager manager = CookieManager.getInstance();
          if (url != null && url.length() != 0) {
          URL myurl = URL.parseString(url);
          Cookie[] cookies = manager.getCookies(myurl);
          HttpDelete delete = new HttpDelete(url);
          if (cookies != null && cookies.length > 0) {
          StringBuilder sb = new StringBuilder();
          for (Cookie ck : cookies) {
          sb.append(ck.name).append('=').append(ck.value).append(";");
          }
          String sck = sb.toString();
          if (sck.length() > 0) {
          delete.setHeader("Cookie", sck);
          }

          }
          delete.setHeader("Accept-Encoding", "gzip, deflate");
          delete.setHeader("Accept-Language", "zh-CN");
          delete.setHeader("Accept", "application/json, application/xml, text/html, text/*, image/*, */*");
          try {
          response = new DefaultHttpClient().execute(delete);
          if (response != null) {
          int statusCode = response.getStatusLine().getStatusCode();
          if (statusCode == 200 || statusCode == 403) {
          Header[] headers = response.getHeaders("Set-Cookie");
          if (headers != null && headers.length > 0) {
          for (Header header : headers) {
          manager.setCookie(myurl, header.getValue());
          }
          }
          in = response.getEntity().getContent();
          if (in != null) {
          bre = ResourceUtil.readStream(in);
          }

          }
          }


          } catch (IOException e) {
          e.printStackTrace();
          }
          }


          return bre;
          }

           

          }










          http://www.cnblogs.com/lianghui66/archive/2013/03/06/2946495.html

          posted @ 2013-04-19 23:39 abin 閱讀(1613) | 評(píng)論 (0)編輯 收藏

          我是一個(gè)初學(xué)者. 
          我建了一個(gè)classes表和一個(gè)students表,表示班級(jí)和學(xué)生,其中學(xué)生里面有一個(gè)外鍵關(guān)聯(lián)到班級(jí)表. 
          然后學(xué)生類里面建了一個(gè)classes的屬性, 
          用session取出學(xué)生后,如果關(guān)閉session的話,就無法讀取到學(xué)生類里的classes值,沒有關(guān)閉就能讀取到. 
          請(qǐng)問這個(gè)session會(huì)不會(huì)影響到其他用戶的訪問呢? 
          就是說如果有兩個(gè)用戶并行操作數(shù)據(jù)庫的話,一個(gè)用戶的session不關(guān)閉影不影響另一個(gè)用戶呢?







          J2EE中最大的一個(gè)觀念就是分層.. 

          session是持久層的東東.不可竄到別的層.. 

          你的這個(gè)問題其實(shí)就是延遲加載的問題. 

          從理論的角度講,最好是用一個(gè)就關(guān)一個(gè).防止資源消耗. 
          但由于hibernate中的延遲加載,所以出現(xiàn)了你的關(guān)閉session的話,就無法讀取到學(xué)生類里的classes值問題. 
          這個(gè)問題可以用Hibernate.initialize()來解決.也可就使用opensessionview的方式.spring中提供了這樣的filter 
          不知道這在使用中有沒有使用spring.用了就比較方便,也不會(huì)出現(xiàn)你所說的 

          引用
          一個(gè)用戶的session不關(guān)閉影不影響另一個(gè)用戶

          因?yàn)閟ession由spring來管理,很安全,不會(huì)出現(xiàn)這個(gè)種并發(fā)問題. 

          如果只是使用了Hibernate的話,那得注意了.你在servlet中直接創(chuàng)建session就可能出現(xiàn)并發(fā)問題,因?yàn)閟ession不是線程安全的,而servlet是多線程的. 
          這時(shí)可以使用ThreadLocal來解決這個(gè)問題. 

          希望對(duì)你有所幫助!



          posted @ 2013-04-19 21:28 abin 閱讀(593) | 評(píng)論 (0)編輯 收藏

          在一個(gè)應(yīng)用程序中,如果DAO層使用Spring的hibernate模板,通過Spring來控制session的生命周期,則首選getCurrentSession 

          使用Hibernate的大多數(shù)應(yīng)用程序需要某種形式的“上下文相關(guān)的”session,特定的session在整個(gè)特定的上下文范圍內(nèi)始終有效。然而,對(duì)不同類型的應(yīng)用程序而言,要給為什么是組成這種“上下文”下一個(gè)定義通常是困難的;不同的上下文對(duì)“當(dāng)前”這個(gè)概念定義了不同的范圍。在3.0版本之前,使用Hibernate的程序要么采用自行編寫的基于ThreadLocal的上下文session(如下面代碼),要么采用HibernateUtil這樣的輔助類,要么采用第三方框架(比如Spring或Pico),它們提供了基于代理(proxy)或者基于攔截器(interception)的上下文相關(guān)session 

          從3.0.1版本開始,Hibernate增加了SessionFactory.getCurrentSession()方法。一開始,它假定了采用JTA事務(wù),JTA事務(wù) 定義了當(dāng)前session的范圍和上下文(scope and context)。Hibernate開發(fā)團(tuán)隊(duì)堅(jiān)信,因?yàn)橛泻脦讉€(gè)獨(dú)立的JTA TransactionManager實(shí)現(xiàn)穩(wěn)定可用,不論是否被部署到一個(gè)J2EE容器中,大多數(shù)(假若不是所有的)應(yīng)用程序都應(yīng)該采用JTA事務(wù)管理。 基于這一點(diǎn),采用JTA的上下文相關(guān)session可以滿足你一切需要。 

          更好的是,從3.1開始,SessionFactory.getCurrentSession()的后臺(tái)實(shí)現(xiàn)是可拔插的。因此,我們引入了新的擴(kuò)展接口 (org.hibernate.context.CurrentSessionContext)和新的配置參數(shù) (hibernate.current_session_context_class),以便對(duì)什么是“當(dāng)前session”的范圍和上下文(scope and context)的定義進(jìn)行拔插。 

          org.hibernate.context.JTASessionContext - 當(dāng)前session根據(jù)JTA來跟蹤和界定。這和以前的僅支持JTA的方法是完全一樣的。 

          org.hibernate.context.ThreadLocalSessionContext - 當(dāng)前session通過當(dāng)前執(zhí)行的線程來跟蹤和界定。 

          這兩種實(shí)現(xiàn)都提供了“每數(shù)據(jù)庫事務(wù)對(duì)應(yīng)一個(gè)session”的編程模型,也稱作一請(qǐng)求一事務(wù)。即Hibernate的session的生命周期由數(shù)據(jù)庫事務(wù)的生存來控制。假若你采用自行編寫代碼來管理事務(wù)(比如,在純粹的J2SE,或者 JTA/UserTransaction/BMT),建議你使用Hibernate Transaction API來把底層事務(wù)實(shí)現(xiàn)從你的代碼中隱藏掉。如果你在支持CMT的EJB容器中執(zhí)行,事務(wù)邊界是聲明式定義的,你不需要在代碼中進(jìn)行任何事務(wù)或session管理操作。 

          1、getCurrentSession()與openSession()的區(qū)別 
          * 采用getCurrentSession()創(chuàng)建的session會(huì)綁定到當(dāng)前線程中,而采用openSession() 
          創(chuàng)建的session則不會(huì) 
          * 采用getCurrentSession()創(chuàng)建的session在commit或rollback時(shí)會(huì)自動(dòng)關(guān)閉,而采用openSession()創(chuàng)建的session必須手動(dòng)關(guān)閉 

          2、使用getCurrentSession()需要在hibernate.cfg.xml文件中加入如下配置: 
          * 如果使用的是本地事務(wù)(jdbc事務(wù)) 
          <property name="hibernate.current_session_context_class">thread</property> 
          * 如果使用的是全局事務(wù)(jta事務(wù)) 
          <property name="hibernate.current_session_context_class">jta</property> 

          在SessionFactory啟動(dòng)的時(shí)候,Hibernate會(huì)根據(jù)配置創(chuàng)建相應(yīng)的CurrentSessionContext,在 getCurrentSession()被調(diào)用的時(shí)候,實(shí)際被執(zhí)行的方法是CurrentSessionContext.currentSession()。在currentSession()執(zhí)行時(shí),如果當(dāng)前Session 為空,currentSession 會(huì)調(diào)用SessionFactory 的openSession。所以getCurrentSession() 對(duì)于Java EE 來說是更好的獲取Session 的方法。 

          sessionFactory.getCurrentSession()可以完成一系列的工作,當(dāng)調(diào)用時(shí),hibernate將session綁定到當(dāng)前線程,事務(wù)結(jié)束后,hibernate將session從當(dāng)前線程中釋放,并且關(guān)閉session,當(dāng)再次調(diào)用getCurrentSession()時(shí),將得到一個(gè)新的session,并重新開始這一系列工作。 
          這樣調(diào)用方法如下: 
          Session session = HibernateUtil.getSessionFactory().getCurrentSession();
          session.beginTransaction();
          Event theEvent = new Event();
          theEvent.setTitle(title);
          theEvent.setDate(theDate);
          session.save(theEvent);
          session.getTransaction().commit();



          不需要close session了 

          利于ThreadLocal模式管理Session 
             早在Java1.2推出之時(shí),Java平臺(tái)中就引入了一個(gè)新的支持:java.lang.ThreadLocal,給我們?cè)诰帉懚嗑€程程序時(shí)提供了一種新的選擇。ThreadLocal是什么呢?其實(shí)ThreadLocal并非是一個(gè)線程的本地實(shí)現(xiàn)版本,它并不是一個(gè)Thread,而是thread local variable(線程局部變量)。也許把它命名為ThreadLocalVar更加合適。線程局部變量(ThreadLocal)其實(shí)的功用非常簡(jiǎn)單,就是為每一個(gè)使用某變量的線程都提供一個(gè)該變量值的副本,是每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)和其它線程的副本沖突。從線程的角度看,就好像每一個(gè)線程都完全擁有一個(gè)該變量。ThreadLocal是如何做到為每一個(gè)線程維護(hù)變量的副本的呢?其實(shí)實(shí)現(xiàn)的思路很簡(jiǎn)單,在ThreadLocal類中有一個(gè)Map,用于存儲(chǔ)每一個(gè)線程的變量的副本。 


          public class HibernateUtil {
              
              private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
              
              //創(chuàng)建一個(gè)局部線程變量
              private static final ThreadLocal<Session> THREAD_LOCAL = new ThreadLocal<Session>();
              
              private static final Configuration cfg = new Configuration();
              
              private static SessionFactory sessionFactory;
              
              /*
               * 取得當(dāng)前session對(duì)象
               
          */
              @SuppressWarnings("deprecation")
              public static Session currentSession() throws HibernateException {
                  Session session = (Session)THREAD_LOCAL.get();
                  if (session == null) {
                      if (sessionFactory == null) {
                          try {
                              cfg.configure(CONFIG_FILE_LOCATION);
                              sessionFactory = cfg.buildSessionFactory();
                          } catch (Exception e) {
                              System.out.println("【ERROR】創(chuàng)建SessionFactory對(duì)象出錯(cuò),原因是:");
                              e.printStackTrace();
                          }
                      }
                      
                      session = sessionFactory.openSession();
                      THREAD_LOCAL.set(session);
                  }
                  return session;
              }

              public static void closeSession() throws HibernateException {
                  Session session = (Session) THREAD_LOCAL.get();
                  THREAD_LOCAL.set(null);
                  if(session != null){
                      session.close();
                  }
              }
          }











          posted @ 2013-04-19 21:22 abin 閱讀(1970) | 評(píng)論 (0)編輯 收藏

          package com.abin.lee.hack;

          import java.io.BufferedInputStream;

          import org.apache.http.HttpHost;
          import org.apache.http.HttpResponse;
          import org.apache.http.client.HttpClient;
          import org.apache.http.client.methods.HttpPost;
          import org.apache.http.entity.StringEntity;
          import org.apache.http.impl.client.DefaultHttpClient;
          import org.junit.Test;

          public class HttpClientVisitTest {

           private static final String HttpUrl = " private static final String HttpHost = "111.111.111.111";
           @Test
           public void testHttpClientVisit() {
            HttpClient httpClient = new DefaultHttpClient();
            HttpPost httpPost = new HttpPost(HttpUrl);
            httpPost.addHeader("Accept", "*/*");
            httpPost.addHeader("Accept-Language", "zh-cn");
            httpPost.addHeader("Referer", HttpUrl);
            httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");
            httpPost.addHeader("Cache-Control", "no-cache");
            httpPost.addHeader("Accept-Encoding", "gzip, deflate");
            httpPost.addHeader("User-Agent",
              "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)");
            httpPost.addHeader("Host", HttpHost);
            httpPost.addHeader("Connection", "Keep-Alive");
          //  HttpHost httpProxy = new HttpHost("222.222.222.222", 1443, "http");
          //  httpClient.getParams().setParameter(AllClientPNames.DEFAULT_PROXY,httpProxy);
            StringBuffer params = new StringBuffer();
            String userName = "abin";
            String passWord= "varyall";
            String userAge= "12345";
            String homeTown= "china beijing";
            params.append("__userName ").append("=").append(userName )
              .append("&").append(passWord").append("=")
              .append(passWord).append("&").append("userAge")
              .append("=").append(userAge).append("&")
              .append("homeTown").append("=")
              .append(homeTown);
            HttpResponse httpResponse = null;
            String result = "";
            try {
             StringEntity reqEntity = new StringEntity(params.toString());
             httpPost.setEntity(reqEntity);
             HttpHost httpTarget = new HttpHost(HttpHost, 80, "http");
             httpResponse = httpClient.execute(httpTarget, httpPost);
             System.out.println("httpResponse=" + httpResponse.getStatusLine());

             BufferedInputStream buffer = new BufferedInputStream(httpResponse
               .getEntity().getContent());
             byte[] bytes = new byte[1024];
             int line = 0;
             StringBuilder builder = new StringBuilder();
             while ((line = buffer.read(bytes)) != -1) {
              builder.append(new String(bytes, 0, line));
             }
             result = new String(builder.toString());
            } catch (Exception e) {
             e.printStackTrace();
            } finally {
             if (httpPost.isAborted()) {
              httpPost.abort();
             }
             httpClient.getConnectionManager().shutdown();
            }
          //   System.out.println("result="+result);

           }

          }


          僅列出標(biāo)題
          共50頁: First 上一頁 13 14 15 16 17 18 19 20 21 下一頁 Last 
          主站蜘蛛池模板: 滕州市| 惠来县| 台东县| 井陉县| 汪清县| 凯里市| 文昌市| 衢州市| 湖口县| 沙坪坝区| 安图县| 安平县| 昌邑市| 都昌县| 常熟市| 嘉定区| 武宁县| 洛宁县| 松潘县| 南部县| 冀州市| 津南区| 逊克县| 密山市| 三河市| 金川县| 寿光市| 板桥市| 西畴县| 太仆寺旗| 安阳县| 阿瓦提县| 西昌市| 天镇县| 峨边| 杨浦区| 铜鼓县| 涪陵区| 南溪县| 安西县| 洮南市|