【问题标题】:How do I listen to a mocked object's event?如何收听模拟对象的事件?
【发布时间】:2009-07-27 20:40:51
【问题描述】:

我正在为控制器做一些单元测试,并且正在模拟业务组件。 BC 有一个公共事件,我让控制器在构造控制器时监听该事件。

我遇到的问题是我不断收到期望错误,说明: “IBC.add_MessageRaised(MessageEventHandler) 预期#:1 实际#:0”。

但是,我对我的测试没有任何期望。我想知道它是否与将控制器设置为监听模拟对象(本例中的 BC)上的事件有关。有没有另一种方法可以让控制器监听来自模拟的事件?

我也在想办法让 mock 引发 MessageRaised 事件,但这可能完全是另一个问题。

代码如下:

业务组件接口

public interface IBC
{
    event MessageEventHandler MessageRaised;
    XmlDocument GetContentXml(string path);
}

控制器

private readonly IBC _bc;

public Controller(IBC bc)
{
    _bc = bc;
    _bc.MessageRaised += MessageWatch;
}
private void MessageWatch(object sender, MessageEventArgs e)
{
    if (MessageRaised != null)
        MessageRaised(sender, e);
}

单元测试

MockRepository Mockery = new MockRepository();
TFactory _tFac;
IView _view;
Presenter _presenter = new Presenter();
IBC _bc = Mockery.DynamicMock<IBC>();
Controller _controller = new Controller(_bc);
_tFac = new TFactory(Mockery);
_tFac.Create(ref _view, ref _presenter, ref _controller);

[Test]
public void View_OnGetContentXmlButtonClick_Should_SetXmlInView()
{
    //SETUP
    XmlDocument xmlDocument = new XmlDocument();
    using ( Mockery.Record() )
    {
        SetupResult.For(_view.FilePath).Return("C:\Test.txt");
        Expect.Call(_bc.GetContentXml("C:\Test.txt")).Return(xmlDocument);
        _view.Xml = xmlDocument.InnerXml;
    }

    //EXECUTE
    using ( Mockery.Playback() )
    {
        _presenter.View_OnGetContentXmlButtonClick();
    }
}

【问题讨论】:

    标签: c# unit-testing rhino-mocks


    【解决方案1】:

    以下代码似乎使用了一个模拟对象,并通过使用它导致记录期望:

    Controller _controller = new Controller(_bc);
    

    您正在像这样使用模拟对象:

    _bc.MessageRaised += MessageWatch;
    

    因此,您已经设置了一个事件处理程序被添加到 _bc.MessageRaised 的期望。这不会发生在播放块中,因此会引发错误。

    另请参阅this question,了解对象何时进入记录状态。老实说,我也不明白为什么如果对象隐式进入记录状态,为什么会有显式记录语法。

    【讨论】:

    • 谢谢!另一个问题对我有所帮助,因为我不知道 BackToRecord() 方法。
    【解决方案2】:

    我通过结合一些东西来让它工作(不完全确定它是如何工作的,但确实如此):

    IEventRaiser _raiser;
    MockRepository Mockery = new MockRepository();
    TFactory _tFac;
    IView _view;
    Presenter _presenter = new Presenter();
    IBC _bc = Mockery.DynamicMock<IBC>();
    _bc.MessageRaised += null;
    _raiser = LastCall.GetEventRaiser();
    Controller _controller = new Controller(_bc);
    Mockery.BackToRecord(_bc,BackToRecordOptions.None);
    _tFac = new TFactory(Mockery);
    _tFac.Create(ref _view, ref _presenter, ref _controller);
    

    这使得问题中的测试工作,以及让我在其他测试中从 Mock 对象引发一个事件,例如:

    [Test]
    public void View_OnGetContentXmlButtonClick_When_FileDoesNotExist_Should_RelayMessage()
    {
        //SETUP
        XmlDocument xmlDocument = new XmlDocument();
        using (Mockery.Record())
        {
            SetupResult.For(_view.FilePath).Return("C:\Test.txt");
            Expect.Call(_bc.GetContentXml("C:\Test.txt")).Return(null);
            _view.Xml = xmlDocument.InnerXml;
            _view.Message = MESSAGE_FILE_NOT_EXIST;
        }
    
        //EXECUTE
        using (Mockery.Playback())
        {
            _presenter.View_OnGetContentXmlButtonClick();
            _raiser.Raise(_bc, new MessageEventArgs(MESSAGE_FILE_NOT_EXIST));
        }
    }
    

    希望其他人觉得这很有用!

    【讨论】:

      【解决方案3】:

      以下是我处理从模拟对象引发事件的方式:

              port.DataPacketReceived += null;
              packetReceivedRaiser =
                  LastCall.IgnoreArguments().Repeat.Any().GetEventRaiser();
      

      在这种情况下,端口是一个模拟对象,带有一个名为 DataPacketReceived 的事件。

      理想情况下,我总是尝试将我的模拟对象置于播放模式,然后再将它们传递给被测系统。这样可以避免任何“意外的期望”。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-01-14
        • 2019-06-14
        • 1970-01-01
        • 2022-08-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多