2009-09-15 79 views
21

我正在開發一個嘗試實現MVP模式using this example的asp.net(經典)應用程序。在試圖單元測試我的演講,並使用以下模式,僞代碼爲它看起來像這樣使用Moq驗證事件註冊

//base view interface 
public interface IView 
{ 
    event EventHandler Init; 

    event EventHandler Load; 

    bool IsPostBack { get; } 

    void DataBind(); 

    bool IsValid { get;} 
} 

//presenter psuedo code 
public class SomePresenter 
{ 
    public SomePresenter(ISomeDomainService service, IView someView) 
    { 
      ... 
      //HOW DO WE TEST/VERIFY THAT THIS REGISTRATION OCCURS? 
      someView.Init += OnInit; 
      someView.Load += OnLoad; 
    } 
} 
... 
//consuming code that exercises the above code, that needs to be tested 
var presenter = new SomePresenter(someDomainService, someView); 

如何確認主持人是做什麼的預期,即註冊的初始化和加載事件?雖然這是很容易使用犀牛嘲笑Phil Haack's example做...

[Test] 
public void VerifyAttachesToViewEvents() 
{ 
    viewMock.Load += null; 
    LastCall.IgnoreArguments(); 
    viewMock.PostSaved += null; 
    LastCall.IgnoreArguments(); 
    mocks.ReplayAll(); 
    new PostEditController(viewMock, 
     this.dataServiceMock); 
    mocks.VerifyAll(); 
} 

...我們如何能做到這一點使用最小起訂量?

+1

+1 - 打我的頭對着同一堵牆。 – Gishu 2009-09-17 10:26:15

回答

14

看來這個功能是moq中的not currently available,但可能會出現在未來的版本中(我看了4.0.812.4測試版,但似乎沒有)。

可能值得提問:「爲什麼SomePresenter需要訂閱查看的LoadInit事件?」推測這是因爲SomePresenter類需要對這些事件做出響應。因此,在您的Mock<IView>上使用Raise方法來提高LoadInit事件可能會更好,然後聲稱SomePresenter做出了正確的迴應。

+1

我想過,基本上有兩個部分來測試這段代碼。 1.交互測試...驗證事件註冊 2.基於狀態的測試...驗證演示者中的事件處理程序的行爲如預期。 乾淨的單元測試將分別測試每種情況。對於 一種解決方案(1)是一對夫婦的方法添加到IVIEW接口 公共接口IVIEW { ... 公共無效RegisterForInit(事件處理程序回調); ... } 和修改演示構造函數 公共SomePresenter(...) { ... someView.RegisterFoInit(的OnInit); } – 2009-09-16 20:20:53

+0

因此,如果涉及事件訂閱,則不能使用來自MOQ的嚴格模擬。 (呻吟) – Gishu 2009-09-17 10:31:27

+0

更新了有關此問題的GitHub問題https://github.com/Moq/moq4/issues/49的鏈接。 – KevM 2015-09-16 15:36:17

0

我花了一些時間這個問題,我用我的項目,該項目的解決方案是:

單元測試:

// Arrange 
TestedObject.Setup(x => x.OnEvent1()); 
TestedObject.Setup(x => x.OnEvent2()); 

// Act 
TestedObject.Object.SubscribeEvents(); 
TestedObject.Raise(x => x.Event1 += null); 
TestedObject.Raise(x => x.Event2 += null); 

// Assert 
TestedObject.Verify(x => x.OnEvent1(), Times.Once()); 
TestedObject.Verify(x => x.OnEvent2(), Times.Once()); 

測試方法:

this.Event1 += OnEvent1; 
this.Event2 += OnEvent2; 

所以,首先你必須嘲笑你分配事件的方法,在給我打電話之後你想測試的方法,最後提出所有訂閱的事件。如果事件是真正訂閱的,您可以使用Moq檢查是否調用了分配的方法。

GLHF!

+4

雖然這很混亂。 OnEvent1()方法位於被測對象上,而Raise()需要在模擬對象上調用。你能發表所有班級的實際工作代碼嗎? – 2016-08-08 22:16:14

0

我知道這對#Dilip來說可能已經太晚了,但這個答案可能對那些正在嘗試做同樣的事情有幫助。 下面是測試類

public delegate void SubscriptionHandler<T>(string name, T handler); 

public class SomePresenterTest 
{ 
    [Test] 
    public void Subscription_Test() 
    { 
     var someServiceMock = new Mock<ISomeDomainService>(); 
     var viewMock = new Mock<IView>(); 
     //Setup your viewMock here 

     var someView = new FakeView(viewMock.Object); 
     EventHandler initHandler = null;    
     someView.Subscription += (n, h) => { if ((nameof(someView.Init)).Equals(n)) initHandler=h; }; 

     Assert.IsNull(initHandler); 

     var presenter = new SomePresenter(someServiceMock.Object, someView); 

     Assert.IsNotNull(initHandler); 
     Assert.AreEqual("OnInit", initHandler.Method?.Name); 
    } 
} 

FakeView是如下實施的裝飾(注意活動:初始化/負載{添加,刪除}):

public class FakeView : IView 
{ 
    public event SubscriptionHandler<EventHandler> Subscription; 
    public event SubscriptionHandler<EventHandler> Unsubscription; 
    private IView _view; 
    public FakeView(IView view) 
    { 
     Assert.IsNotNull(view); 
     _view = view; 
    } 

    public bool IsPostBack => _view.IsPostBack; 
    public bool IsValid => _view.IsValid; 

    public event EventHandler Init 
    { 
     add 
     { 
      Subscription?.Invoke(nameof(Init), value); 
      _view.Init += value; 
     } 

     remove 
     { 
      Unsubscription?.Invoke(nameof(Init), value); 
      _view.Init -= value; 
     } 
    } 
    public event EventHandler Load 
    { 

     add 
     { 
      Subscription?.Invoke(nameof(Load), value); 
      _view.Init += value; 
     } 

     remove 
     { 
      Unsubscription?.Invoke(nameof(Load), value); 
      _view.Init -= value; 
     } 
    } 

    public void DataBind() 
    { 
     _view.DataBind(); 
    } 
}