Unit Test Via Visual Studio-Part5

來源:互聯網
上載者:User

標籤:

本文主要介紹Visual Studio(2012+)單元測試架構的一些技巧:

  1. 如何類比類的靜態建構函式
  2. 如何測試某方法被調用過
  3. 如何測試某方法執行的次數
  4. 並行編程測試注意事項
一、如何類比類的靜態建構函式1.1 被測代碼
namespace BlogDemo.UTDemo.Tricks{    public class StaticConstructorExample    {        static StaticConstructorExample()        {            //step1:read data from web service            //step2:save data into database        }        public int DoSomething()        {            return 0;        }    }}

上面的類有一個靜態建構函式,裡面做兩件事情:

  1. 從某webservice讀取資料
  2. 將讀取到的資料儲存到資料庫。

以上兩件事情都是對外界環境的嚴重依賴

我們需要測試的方法是DoSomething,需要測試這個類,就需要執行個體化StaticConstructorExample這個類,執行個體化這個類之前,其靜態建構函式將會被自動執行:這個點就是問題。

  1. 靜態建構函式依賴外部環境,這個外部環境有可能在某一天就掛了
  2. 如果外部環境掛了,靜態建構函式就會報錯,那我們的單元測試就無法按照預期進行運行
  3. DoSOmething本身不直接依賴外部環境,但是通過靜態建構函式間接依賴了外部環境,這違反了單元測試repeatable原則(當外部環境掛了,我們的測試就無法運行)

解決辦法依然是Shim,通過ms.test的Shim可以類比一個類的靜態建構函式,從而改變靜態建構函式的行為,消除外部依賴。

1.2 測試代碼
 [TestClass]    public class StaticConstructorExampleTests    {        [TestMethod]        public void DoSomethingTest()        {            using (ShimsContext.Create())            {                var isStaticConstructorExecuted = false;                ShimStaticConstructorExample.StaticConstructor = () =>                {                    //step1:mock web service                    //step2:mock database                    isStaticConstructorExecuted = true;                };                var example = new StaticConstructorExample();                var data = example.DoSomething();                Assert.AreEqual(0, data);                Assert.IsTrue(isStaticConstructorExecuted);            }                   }    }

上面的代碼是通過ShimClassName.StaticConstructor來實現對靜態建構函式進行類比的。

二、如何測試某方法被調用過2.1 被測代碼

基礎代碼,在DemoClass中進行調用

    public interface IGoodActionHandler    {        void Action();    }    public interface IBadActionHandler    {        void Action();    }    public class GoodHandler : IGoodActionHandler    {        public void Action()        {            throw new NotImplementedException();        }    }    public class BadHandler : IBadActionHandler    {        public void Action()        {            throw new NotImplementedException();        }    }

 要被測試的類:

  public class DemoClass    {        IGoodActionHandler goodHandler;        IBadActionHandler badHandler;        public DemoClass()        {            this.goodHandler = new GoodHandler();            this.badHandler = new BadHandler();        }        public void DoSomething(int type)        {            if (type == 1)            {                DoSomethingGood();            }            else            {                DoSomethingBad();            }        }        private void DoSomethingGood()        {            this.goodHandler.Action();        }        private void DoSomethingBad()        {            this.badHandler.Action();        }    }

要測試上面的DoSomething方法,上面DoSomethingGood和DoSomethingBad都依賴自介面,在測試的時候都可以進行mock(stub)。在側測DoSomething方法是只需要驗證當Type為1時執行了goodHandler的action,否則執行badHandler的Action。

這也是單元測試的一個關鍵點:關注單元。這裡不關注goodhandler 和badhandler的內部邏輯(這兩個handler的內部邏輯可以單獨測試,屬於另外的單元),這裡只關注是否按照邏輯路由到了正確的handler。

2.2測試代碼
  [TestClass]    public class DemoClassTests    {        [TestMethod]        public void DoSomething_DoSomethingGood_Tests()        {            var goodHandlerExecuted = false;            StubIGoodActionHandler goodHandler = new StubIGoodActionHandler()            {                Action = () =>                {                     goodHandlerExecuted = true;//如果執行了goodhandler則置為true                }            };            var badHandlerExecuted = false;            StubIBadActionHandler badHandler = new StubIBadActionHandler()            {                Action = () =>                {                    badHandlerExecuted = true;//如果執行了badhandler則置為true                }            };            var demoClass=new DemoClass();            demoClass.BadHandler=badHandler;//注入badhandler            demoClass.GoodHandler=goodHandler;//注入goodhandler            demoClass.DoSomething(1);            Assert.IsTrue(goodHandlerExecuted);//執行了goodhandler            Assert.IsFalse(badHandlerExecuted);//沒有執行badhandler        }    }

上面使用了兩個技術

  1. 面向介面編程,使用Stub技術,動態注入Stub(打樁),在測試的時候對對象進行了類比
  2. 改變了Stub對象的行為,本文只是通過在Stub對象的內部設定標誌位的值來表示是否執行了這一個步驟。

上面的標誌位的方法Shim技術照樣使用,靜態建構函式的類比就是通過Shim後在建構函式內部修改標識位來驗證建構函式被執行的。接下來介紹的測試執行次數也是通過shim後修改標識位來實現的

三、如何測試某方法執行的次數3.1 被測代碼
  public class DemoClass    {        private LoopHandler handler;        public DemoClass()        {            handler = new LoopHandler();        }        public void DoSomething(int times)        {            for (var i = 0; i < times; i++)            {                handler.Do();            }        }    }    public class LoopHandler    {        public void Do() { }    }

現在要驗證DoSomething是否執行了times次Do方法。

3.2 測試代碼
 [TestMethod]        public void DoSomething_RunTimes_Test()        {            using(ShimsContext.Create())            {                var times = 0;                ShimLoopHandler.AllInstances.Do = (@this) =>                {                    times++;//每進入一次加一次                };                var givenTimes = 100;                new DemoClass().DoSomething(givenTimes);                Assert.AreEqual(times, givenTimes);            }        }

思想和上面Stub是一樣,在Shim類比的方法內部進行計數器相加。

3.3 並行編程測試注意事項

這裡有一點需要注意一下,因為int的++不是安全執行緒的,如果把DemoClass的迴圈修改成多個線程並存執行的話測試代碼需要做相應的調整。,對times++進行lock。

 public void DoSomething(int times)        {            Parallel.For(0, times, (item) =>//並存執行            {                handler.Do();            });        } [TestMethod]        public void DoSomething_RunTimes_Test()        {            using(ShimsContext.Create())            {                var times = 0;                ShimLoopHandler.AllInstances.Do = (@this) =>                {                    lock (this)//lock安全執行緒                    {                        times++;//每進入一次加一次                    }                };                var givenTimes = 100;                new DemoClass().DoSomething(givenTimes);                Assert.AreEqual(times, givenTimes);            }        }

上面的代碼當Times不是特別大的時候一般不加lock也是可以的,不是特別大的時候一般不會產生安全執行緒問題。但是當times比較大的時候不加lock就會出問題。

當times是100000時去掉lock。我運行就出現了問題:

測試就是為了嚴謹,如果有上面並行的問題,測試的時候還是加上lock比較好。

Unit Test Via Visual Studio-Part5

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.