文章目錄
Unit Test 該拿 private 屬性及欄位怎麼辦? - 使用 PrivateObject
在 Unit Test 該拿 static 屬性及欄位怎麼辦? - 使用 PrivateType 使用 NSubstitute 產生假物件後,透過 PrivateType 設定給原本是 private static 的屬性,但文末也提供 PrivateType 是針對 static field 或是 property 才能使用
如果只是一般的 field 或是 property 該如何是好呢? 這時候可以使用 PrivateObject
基本環境
有個 restful api,Get 方法會依據 Web.config 的一個變數值來決定回應內容:如果是 localhost 表示 config 異常,其他值就正常回應
程式碼
public class ValuesController : ApiController { private readonly string connectionString = System.Configuration.ConfigurationManager.AppSettings["connectStr"]; public IHttpActionResult Get() { if (connectionString.Equals("localhost")) return BadRequest(); else return Ok(); } }
Web.config 設定
<configuration> <appSettings> <add key="connectStr" value="db.yowko.com"/> </appSettings> </configuration>
使用 PrivateObject 來模擬 private field or property
寫法 一:直接建立測試目標實體
建立測試目標程式的實體
var target = new ValuesController();
使用測試目標實體建立 PrivateObject
PrivateObject valueController=new PrivateObject(target);
設定測試目標的 field or property
valueController.SetFieldOrProperty("connectionString", "localhost");
呼叫測試目標方法取得回應並進行驗證
//act var actualAction = target.Get(); var actual = actualAction as BadRequestResult; //assert Assert.IsNotNull(actual); Assert.IsInstanceOfType(actual, expected);
完整測試程式碼
[TestMethod()] public void Get_connectionString_is_localhost_Return_BadRequestResult() { //arrange var target = new ValuesController(); PrivateObject valueController=new PrivateObject(target); valueController.SetFieldOrProperty("connectionString", "localhost"); var expected = typeof(BadRequestResult); //act var actualAction = target.Get(); var actual = actualAction as BadRequestResult; //assert Assert.IsNotNull(actual); Assert.IsInstanceOfType(actual, expected); }
寫法 二:使用型別建立 PrivateObject
使用測試目標實體建立 PrivateObject
PrivateObject target = new PrivateObject(typeof(ValuesController));
設定測試目標的 field or property
target.SetFieldOrProperty("connectionString", "localhost");
呼叫測試目標方法取得回應並進行驗證
//act var actualAction = target.Invoke("Get"); var actual = actualAction as BadRequestResult; //assert Assert.IsNotNull(actual); Assert.IsInstanceOfType(actual, expected);
完整測試程式碼
[TestMethod()] public void Get_connectionString_is_localhost_Return_BadRequestResult() { //arrange PrivateObject target = new PrivateObject(typeof(ValuesController)); target.SetFieldOrProperty("connectionString", "localhost"); var expected = typeof(BadRequestResult); //act var actualAction = target.Invoke("Get"); var actual = actualAction as BadRequestResult; //assert Assert.IsNotNull(actual); Assert.IsInstanceOfType(actual, expected); }
心得
PrivateType 用來處理 static 資源 (field,property,method),PrivateObject 則用來處理其他 non-static 資源,還算是清楚,但建立物件方式都不同,是比較容易搞混的
不過我滿欣賞它們不用動到 production code 的特性,讓測試歸測試,重構歸重構,不會為了需要測試得先重構
PrivateObject 的官方說明可以參考 PrivateObject Class
參考資訊
文章作者 Yowko Tsai
上次更新 2021-10-14
授權合約
本部落格 (Yowko's Notes) 所有的文章內容(包含圖片),任何轉載行為,必須通知並獲本部落格作者 (Yowko Tsai) 的同意始得轉載,且轉載皆須註明出處與作者。
Yowko's Notes 由 Yowko Tsai 製作,以創用CC 姓名標示-非商業性-相同方式分享 3.0 台灣 授權條款 釋出。