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(); 
    } 
}