0%

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

Demo檔案 : Git傳送門
請參照UnitTest_Day5的Branch

延續昨日的進度,我們將呼叫API的RestSharp獨立出來,並且用依賴介面及外部注入的方式將IRestSharp注入到PTX來達成隔離,並且做到可測試性。

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

來看看另一種方式

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

撰寫IRestSharpFactory

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

接著修改原本的PTX.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class PTX
{
IRestSharp _MyRestSharp
{
get
{
//這邊改成用工廠建立MyRestSharp實體
return IRestSharpFactory.Generate();
}
}

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

以下省略.....
}

這邊馬上面臨到一個問題,那我們怎麼Mock IRestSharp,昨天是放在建構子中並透過Nsubstitute來Mock

封裝性變好了,但也變得難以介入模擬外部行為

從IRestSharpFactory著手吧!!

想辦法讓IRestSharpFactory可以讓我們注入Mock的假物件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/// <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是可以很多組的。

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

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