我如何收听模拟对象的事件?

时间:2009-07-27 20:40:52

标签: c# unit-testing rhino-mocks

我正在为控制器做一些单元测试,而我正在嘲笑业务组件。 BC有一个公共事件,我在控制器构建时让控制器监听。

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

但是,在我的测试中,我没有任何期望。我想知道它是否与设置Controller以监听模拟对象(在这种情况下为BC)上的事件有关。有没有其他方法可以让Controller听取来自模拟的事件?

我也试图想办法让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();
    }
}

3 个答案:

答案 0 :(得分:3)

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

Controller _controller = new Controller(_bc);

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

_bc.MessageRaised += MessageWatch;

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

另请参阅this question有关对象何时进入记录状态的信息。老实说,如果对象无论如何都隐式进入记录状态,我也不明白为什么会有明确的记录语法。

答案 1 :(得分:0)

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

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

希望别人觉得这很有用!

答案 2 :(得分:0)

以下是我如何处理从模拟对象中引发事件的方法:

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

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

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