前面說了手工創建模擬對象的示例,這一章主要說一下使用模擬框架創建示例。我使用的模擬框架是NSubstitute
我們知道手動編寫偽對象顯得很笨拙(當然按照實際情況),那么用隔離框架創建偽對象就會很簡單快速,那么我們先看一幅圖
那么首先我們創建一個個接口
1 public interface IUser2 {3 bool Add(string userName, string realName);4 }
實現接口
1 public class User:IUser {2 public bool Add(string userName, string realName)3 {4 return true;5 }6 }
業務中調用
1 public class UserManager 2 { 3 public bool Add(string userName, string realName) 4 { 5 if (string.IsNullOrWhiteSpace(userName))throw new ArgumentNullException(userName); 6 if (string.IsNullOrWhiteSpace(realName)) throw new ArgumentNullException(realName); 7 8 if (userName.Length>=0) 9 {10 return false;11 }12 return GetUser().Add(userName, realName);13 }14 }
單元測試
1 [Test]2 public void Add_Default_CallAdd()3 {4 var fakeUser = Substitute.For<IUser>();5 fakeUser.Add("1", "1").Returns(true);//按照我們的意愿進行返回值 6 Assert.IsTrue(fakeUser.Add("1", "1")); 7 }
以上是我們輸入2個參數然后按我們意愿返回true;
如果我們不考慮參數呢
那么只需要把參數置為Arg.Any<T>()//T表示參數的類型可以string,int等看下測試結果
當然返回值一定是按照你預定的類型。比喻bool Add() 返回值必須是一個bool類型的或者拋出異常。
when do在NSub的用處還是比較廣的。比喻當什么的時候做什么。下面一個例子來看一下
1 [Test]2 public void Add_Default_CallAdd()3 {4 var fakeUser = Substitute.For<IUser>(); 5 fakeUser.When(x => x.Add("1", "2")).6 Do(content => { throw new Exception("fack exception"); });//用when,do表示式,表示當操作這個函數的時候會出現什么后果 7 Assert.Throws<Exception>(() => fakeUser.Add("1","2")); 8 }
上面的例子就是當參數是1和2的時候會拋出一個異常。下面看現實的結果
剛剛參數是1和2會拋出一個異常如果是1和3會不會拋出異常呢,我們手動把參數進行修改在看下面效果
我們一看測試通不過,此時說明只有當參數1和2的時候才會拋出異常這樣一來模擬框架也會給我們預定的參數進行一個驗證。
Received()方法工作很神奇,這個方法在什么對象被調用就會返回和這個對象同樣類型的對象,但實際上就是申明斷言的對象。什么意思呢我們看下面的代碼
測試中的接口和實現方法參考上一篇
1 [Test] 2 public void RecordLog_EmailServiceThrows_CallEmail() 3 { 4 var stub = Substitute.For<ILogService>(); 5 var mock = Substitute.For<IEmailService>(); 6 stub.When(p => p.ErrorLog(Arg.Any<String>())).Do(a => { throw new ArgumentNullException(); }); 7 var test = new TestUserManager(stub, mock); 8 test.RecordLog("");//觸發 9 mock.Received().SendEmail(Arg.Is("lp"), Arg.Is("subject"), Arg.Is("值不能為 null。"));//保證接受了方法并調用(不可以少一個句號) 10 }
在以上的例子我們可以看出Received()就表示SendEmail被調用并且收獲到SendEmail發送的信息然后進行斷言是否和預定發送的信息保持一致。這個和上一篇說的模擬對象斷言是一樣的。(如果使用模擬對象不要忘記Received)
4.1:測試監聽事件
定義一個事件的接口
1 public interface IView2 {3 event Action Loaded;4 void Render(string content);5 }
事件相關的代碼并如何觸發事件的
1 public class PResenter 2 { 3 4 private readonly IView _view; 5 6 public Presenter(IView view) 7 { 8 this._view = view; 9 this._view.Loaded += Onload;//注冊事件10 }11 12 public void Onload()13 {14 _view.Render("Hello World");15 }16 }
測試代碼
1 [Test] 2 public void Ctor_WhenViewIsLoad_CallsViewRender() 3 { 4 var mockView = Substitute.For<IView>(); 5 var p =new Presenter(mockView); 6 mockView.Loaded += Raise.Event<Action>(); 7 8 mockView.Received().Render(Arg.Is<string>(s=>s.Contains("Hello World"))); 9 10 }
Raise.Event表示觸發事件。
最后使用received來測試是否view中的Render是否觸發,如果真的觸發那么接受的參數是否是Hello World。
我們可以模擬一個存根一個一個模擬對象
修改與事件相關的代碼
1 public class Presenter 2 { 3 4 private readonly IView _view; 5 private readonly ILogService _logService; 6 7 public Presenter(IView view,ILogService logService) 8 { 9 this._view = view;10 this._logService = logService;11 this._view.Loaded += Onload;//注冊事件12 this._view.ErrorOccured += WriteLog;13 }14 15 public void Onload()16 {17 _view.Render("Hello World");18 }19 20 public void WriteLog(string errorContent) {21 _logService.ErrorLog(errorContent);22 }23 }
測試代碼
1 [Test] 2 public void Ctor_WhenViewIsError_CallsLogs() { 3 4 var stubView = Substitute.For<IView>(); 5 var mockLog = Substitute.For<ILogService>(); 6 var p = new Presenter(stubView,mockLog); 7 stubView.ErrorOccured += Raise.Event<Action<string>>("fack error"); 8 mockLog.Received().ErrorLog(Arg.Is<string>(s => s.Contains("fack error"))); 9 10 }
注意:使用存根stubView.ErrorOccured += Raise.Event<Action<string>>("fack error");來觸發事件,然后在用模擬對象查看日志服務是否被正確的調用
4.2:測試事件是否觸發
這個比較簡單,在測試方法內部使用一個匿名委托,手動注冊次方法即可
定義一個變量,當事件觸發改變變量的值即可。
如:bool isTrigger=false;
SomeView=new SomeView();
SomeView.Load+=delegate{isTrigger=true;}
SomeView.觸發事件 然后比對isTrigger的值是否改變。
測試代碼不可讀
驗證錯誤的事情
一個測試可能會有多個模擬對象
過度指定
NSubstitute的官方文檔很詳細有興趣的可以自己查看動手寫一寫會有更好的效果http://nsubstitute.github.io/help/raising-events/
新聞熱點
疑難解答