VS2012 Unit Test——Microsoft Fakes入門


如題,本文主要作為在VS2012使用Fakes的入門示例,開發工具必須是VS2012或更高版本。

關於Fakes的MSDN地址:http://msdn.microsoft.com/en-us/library/hh549175.aspx

關於VS2012單元測試的前期文章:

1.《在Visual Studio 2012使用單元測試》、

2.《VS2012 單元測試之泛型類(Generics Unit Test)》、

3.《VS2012 Unit Test —— 我對接口進行單元測試使用的技巧

4.《VS2012 Unit Test(Void, Action, Func) —— 對無返回值、使用Action或Func作為參數、多重載的方法進行單元測試

 

依我個人理解單元測試就是對程序的小單元進行測試,一個測試不應包含兩個或更多單元,總體而言大多都是對方法、屬性的編碼正確性進行驗證。但是往往一個方法又會調用其他的方法或屬性,我這里暫稱之為外部依賴,因而外部依賴會影響程序單元的測試結果,要避免這樣的情況就不得不使用一些外部依賴的模擬進行隔離(Isolate),本文就是使用了Microsoft Fakes,當然還有其他更為流行的框架可以選擇使用(Moq、Rhino Mocks、Type Mock)

 

Fakes有兩種形式:stub 和 shim。具體的介紹我就不啰嗦,因為我英文不好可能會表達錯誤誤導新人。

我的Demo也是看了MSDN后以個人理解后進行簡單的編寫,如果MSDN看懂了也就不用看以下內容了,期待和我一樣正在使用VS2012 MSTest進行單元測試的一起交流進步。

 

一、shim

以下將模擬DateTime的Now屬性,假設我現在需要在活動服務類ActivityService添加一個方法驗證某個線下活動是否過期。

1. 打開VS2012,創建單元測試項目FakesTesting,我這是測試先行。重命名項目自動生成的類UnitTest1為ActivityServiceTest,將TestMethod1改為IsExpireTest(是否過期).

2. 添加代碼“ActivityService service = new ActivityService();”並使用VS快捷功能為我們創建ActivityService 類

3. 添加Fakes,由於DateTime位於System程序集,因而將添加System的Fake程序集(右鍵System程序集),  然后在測試類“using System.Fakes;”

4.  編寫測試代碼如下

using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Fakes;
using Microsoft.QualityTools.Testing.Fakes;

namespace FakesTesting.Test
{
    [TestClass]
    public class ActivityServiceTest
    {
        [TestMethod]
        public void IsExpireTest()
        {
            ActivityService service = new ActivityService();
            bool actual = service.IsExpire();
            Assert.IsFalse(actual);

            using (ShimsContext.Create())
            {
                ShimDateTime.NowGet = () => new DateTime(2014, 5, 5);
                actual = service.IsExpire();
                Assert.IsFalse(actual);
            }
        }
    }
}

5. 然后編寫ActivityService類

    public class ActivityService
    {
        public DateTime BeginTime { get; set; }

        public ActivityService()
        {
            this.BeginTime = new DateTime(2014, 3, 3);  //僅作演示,無意義
        }

        public bool IsExpire()
        {
            return BeginTime >= DateTime.Now;
        }
    }

6. 運行測試通過。然后就可以把實際業務類移動到相應VS項目中,並調整命名空間。

 

二、Stub

現在假設ActivityService類有一個方法獲取是否還能報名,但是它依賴於倉儲IActivityRepository(只有遵循依賴反轉與接口隔離原則的代碼才好使用Stub填充外部依賴)提供的RegisterNumber方法。

1. IActivityRepository接口(新建IRepositories項目並添加該接口)

    public interface IActivityRepository
    {
        /// <summary>
        /// 已報名人數
        /// </summary>
        int RegisterNumber();
    }

2. 而我們的單元測試現在不能依賴具體(實際環境中的Repository可能對測試帶來影響),這時候就能使用Stub來填充該接口了,添加IRepositories引用,然后與上一個Demo一樣的添加IRepositories的Fakes程序集。

3. 在測試類中添加Using代碼

using IRepositories;
using IRepositories.Fakes;

4. 編寫測試代碼

        [TestMethod]
        public void CanRegisterTest()
        {
            StubIActivityRepository repository = new StubIActivityRepository();
            ActivityService service = new ActivityService(repository);

            //如果已報名人數小於最多可報名數量則不能再報名,斷言CanRegister方法應為True
            repository.RegisterNumber = ()=> 20;
            bool actual = service.CanRegister();
            Assert.IsTrue(actual);

            //如果已報名人數大於等於最多可報名數量則不能再報名,斷言CanRegister方法應為False
            repository.RegisterNumber = () => 50;
            actual = service.CanRegister();

        Assert.IsFalse(actual);
      }

 
        

5. ActivityService代碼:

    public class ActivityService
    {
        public DateTime BeginTime { get; set; }

        /// <summary>
        /// 最多可報名數量
        /// </summary>
        private int maxCount = 50;
        private IActivityRepository repository;

        public ActivityService()
        {
            this.BeginTime = new DateTime(2014, 3, 3);  //僅作演示,無意義
        }

        public ActivityService(IActivityRepository repository)
        {
            // TODO: Complete member initialization
            this.repository = repository;
        }
        
        public bool IsExpire()
        {
            return BeginTime >= DateTime.Now;
        }

        public bool CanRegister()
        {
            return repository.RegisterNumber() < this.maxCount;
        }
    }

 

總結

stub用於我們可控的代碼,shim用於不可控的,例如.NET Framework以及第三方類庫等。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM