qileilove

          blog已經轉移至github,大家請訪問 http://qaseven.github.io/

          單元測試—使用模擬對象做交互測試

            最近在看.net單元測試藝術,我也喜歡單元測試,這里寫一下如何在測試中使用模擬對象。
            開發的過程中,我們都會遇到對象間的依賴,比如依賴數據庫或文件,這時,我們需要使用模擬對象,來進行測試,我們可以手寫模擬對象,當然也可以使用模擬框架。
            假如有這樣的一個需求,當用戶登陸時,我需要對用戶名和密碼進行驗證,然后再將用戶名寫入日志中。
          public class MyLogin
          {
          public ILog Log { get; set; }
          public bool Valid(string userName, string passWord)
          {
          var isValid = userName == "admin" && passWord == "123456";
          Log.Write(userName);
          return isValid;
          }
          }
          public interface ILog
          {
          void Write(string message);
          }
            上面的代碼在驗證完登陸信息后,需要向日志中寫入用戶名,由于寫入日志可能依賴于文件或數據庫,我們可能很難進行測試,所以,這里使用模擬對象進行測試。手寫模擬對象,代碼如下:
          public class MyLoginTest
          {
          [Test]
          public void Vaild_Test()
          {
          MyLogin login = new MyLogin();
          var log = new TestLog();
          login.Log = log;
          var userNmae = "admin";
          var passWord = "123456";
          var isLogin = login.Valid(userNmae, passWord);
          Assert.AreEqual(isLogin, true);
          Assert.AreEqual(log.Message, userNmae);
          }
          }
          public class TestLog : ILog
          {
          public string Message;
          public void Write(string message)
          {
          this.Message = message;
          }
          }
            這里我們定義了一個對象TestLog對象,該對象就是一個模擬對像,繼承了ILog接口。該測試中,一共進行了兩項測試。一項是:驗證用戶名和密碼是否輸入正確。另一項是:驗證用戶寫入日志的信息是否正確(比如應該寫入用戶名,結果把密碼寫入了日志,測試會無法通過)。
            這里我們區分一下模擬對象與樁對象。上一節中,我們講過樁對象的定義,那么模擬對象與樁對象是什么關系呢?
            模擬對象與樁對象在寫法上區別很小,關鍵在于模擬對象需要進行斷言,也就是說模擬對象可以導致測試失敗。樁對象只是為了方便測試所定義的一個對象,不需要進行斷言,所以,樁對象永遠不會導致測試失敗。
            上面的測試中,如果我們去掉最后一行代碼,即我們不進行寫入日志的斷言,則該對象就是一個樁對象。
            Assert.AreEqual(log.Message, userNmae);
            上面的模擬對象是我們自己寫的,自己寫模擬對象比較費時,我們可以使用模擬框架進行編寫。這里我使用了Rhino Mocks框架。如果要執行下面的代碼,需要下載Rhino.Mocks.dll文件,然后直接引用即可。
            測試框架這里我選用了NUnit框架。測試代碼如下:
          [TestFixture]
          public class MyLoginTest
          {
          [Test]
          public void Mock_Vaild_Test()
          {
          MockRepository mock = new MockRepository();
          var log = mock.DynamicMock<ILog>();
          var userName = "admin";
          var passWord = "123456";
          using (mock.Record())
          {
          log.Write(userName);
          }
          MyLogin login = new MyLogin();
          login.Log = log;
          var isLogin = login.Valid(userName, passWord);
          Assert.AreEqual(isLogin, true);
          mock.VerifyAll();
          }
            這里我沒有編寫一個類去繼承ILog接口,而是通過模擬框架,動態生成了一個ILog對象。代碼是這句:
            MockRepository mock = new MockRepository();
            var log = mock.DynamicMock<ILog>();
            這里便生成了Log對象。通過錄制-回放的模式進行模擬對象測試,首先需要定義我們的期望行為,最后驗證實際行為與期望行為是否一致。這里,需要錄制我們期望行為,代碼如下:
          using (mock.Record())
          {
          log.Write(userName);
          }
            這里我們期望向日志中寫入用戶名。再通過回放來進行驗證,代碼如下:
            mock.VerifyAll();
            該方法會驗證,期望向日志中寫入的信息與實際向日志中寫入的信息是否一致,如果不一致,測試失敗。
            這里我們便完成了使用模擬框架進行單元測試。如果我們不需要測試日志寫入方法,則把模擬對象換成樁對象就可以了,生成樁對象的方法如下:
            MockRepository mock = new MockRepository();
            var log = mock.Stub<ILog>();
            把回放的方法(mock.VerifyAll())去掉,就完成了模擬對象向樁對象的轉變。注意,這里錄制的代碼還是需要的。
            總結:編寫模擬對象和樁對象是非常有意義的,使用框架可以幫助我們簡化單元測試。一般情況下,一個測試中,可以有多個樁對象,但最好只有一個模擬對象。模擬對象太多,證明一個測試方法做了太多項測試,不利于維護測試代碼,一旦代碼變改,很容易使單元測試失敗。
            下一節,寫一下測試框架的一些常用功能,如:如何模擬異常、如何模擬返回值等。。。

          posted on 2014-06-10 09:48 順其自然EVO 閱讀(275) 評論(0)  編輯  收藏


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


          網站導航:
           
          <2014年6月>
          25262728293031
          1234567
          891011121314
          15161718192021
          22232425262728
          293012345

          導航

          統計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 大邑县| 桐柏县| 安平县| 奉贤区| 亳州市| 光山县| 清徐县| 南雄市| 嘉黎县| 苏尼特右旗| 鄯善县| 冕宁县| 新田县| 襄汾县| 商南县| 沈阳市| 嘉荫县| 句容市| 阳东县| 邓州市| 长寿区| 涟源市| 扬中市| 喜德县| 赫章县| 新野县| 忻城县| 柯坪县| 确山县| 嘉鱼县| 巴楚县| 桐梓县| 海南省| 东兰县| 阳曲县| 高邑县| 乌兰浩特市| 广饶县| 米易县| 丽水市| 宁晋县|