2017年4月20日 星期四

【Unit Test】Day 5 - 透過InternalsVisibleTo來達成單元測試的外部注入


Demo檔案 : Git傳送門
請參照UnitTest_Day5的Branch
延續昨日的進度,我們將呼叫API的RestSharp獨立出來,並且用依賴介面及外部注入的方式將IRestSharp注入到PTX來達成隔離,並且做到可測試性。



但面臨令一個問題,如果今天開發的是共用套件類的專案,這樣變成要使用物件都必須知道該用什麼東西注入才可使用,這往往會造成使用者困擾,封裝性也不佳,因外部注入的IRestSharp是每個人都可以另外實作的。 甚至更深一層去想,究竟IRestSharp是否需要用Public讓外部使用者都知道有這個東西呢? 是不是反而因為要達到可測試性而讓封裝這件事做得更差?

來看看另一種方式

假設我認為使用這個套件的人只要簡單的建立PTX後即可使用,至於內部如何呼叫API的實作使用者並不需要關心,那麼我們先將IRestSharp從建構子拿掉吧,並且做一個專門產生IRestSharp實體的Factory來滿足需求。

撰寫IRestSharpFactory
    
    /// <summary>
    /// 製作IRestSharp的工廠
    /// </summary>
    internal class IRestSharpFactory
    {
        //這邊特別注意存取修飾子是用Internal
        //原因是我並不希望專案之外的人使用且知道有這東西
        //而internal剛好能滿足這需求
        internal static IRestSharp Generate()
        {
            //產生實體
            return new MyRestSharp();
        }
    }

接著修改原本的PTX.cs
    public class PTX
    {
        IRestSharp _MyRestSharp
        {
            get
            {
                //這邊改成用工廠建立MyRestSharp實體
                return IRestSharpFactory.Generate();
            }
        }

        /// <summary>
        /// Construct
        /// </summary>
        public PTX()
        {
            //建構子參數移除
        }

        以下省略.....
    }

這邊馬上面臨到一個問題,那我們怎麼Mock IRestSharp,昨天是放在建構子中並透過Nsubstitute來Mock
封裝性變好了,但也變得難以介入模擬外部行為

從IRestSharpFactory著手吧!!
想辦法讓IRestSharpFactory可以讓我們注入Mock的假物件
    
    /// <summary>
    /// 製作IRestSharp的工廠
    /// </summary>
    internal class IRestSharpFactory
    {
        /// <summary>
        /// 此屬性只供UnitTest注入
        /// </summary>
        internal static IRestSharp _IRestSharpForUnitTest;

        internal static IRestSharp Generate()
        {
            //如果這個值不為Null,則表示單元測試所注入,直接回傳
            if (_IRestSharpForUnitTest != null)
            {
                return _IRestSharpForUnitTest;
            }

            //產生實體
            return new MyRestSharp();
        }
    }

這邊開了一個_IRestSharpForUnitTest屬性,讓外部能夠注入它,接著在Generate的方法中判斷,如果當_IRestSharpForUnitTest不為Null時,直接回傳(通常會特別在這個屬性寫上說明僅供單元測試使用,正常Production Code禁止使用!!)

想辦法在單元測試中注入_IRestSharpForUnitTest
你可能會發現在單元測試中看不到IRestSharpFactory....

原因是我們宣告成Internal,如果要能在專案之外看到就只能開成Public了,但回到最一開始討論的,不就是為了封裝才把他宣告成Internal嗎?如果又改回Public那我們這段工不就白費了,還好還有別的方法可以達成。


告訴UnitTestDay3這個專案,除了它自己之外,還有誰能看到它宣告成Internal的類別與屬性方法

讓我們先打開UnitTestDay3專案的AssemblyInfo
寫下這行

InternalsVisibleTo這邊是要填AssemblyName,透過這個Attribute告訴UnitTestDay3這個專案還有誰能看到它內部的Internal。
而AssemblyName怎麼看呢?在專案上右鍵 > 屬性


如果你有很多單元測試專案需要能看到,InternalsVisibleTo是可以很多組的。


接著在單元測試中就可以看到啦


這樣就達成我們想封裝起來的需求,卻也能讓單元測試進行注入,那今天就談到這吧!!

0 意見:

張貼留言