views:

121

answers:

3

I'm doing some unit tests for a controller, and I'm mocking the business component. The BC has a public event that I have the controller listening to when the controller is constructed.

The problem I'm having is I keep getting an Expectation error stating: "IBC.add_MessageRaised(MessageEventHandler) Expected#:1 Actual#:0".

However, I don't have any expectation of that kind in my test. I'm wondering if it has to do with setting the Controller to listen to an event on a mocked object (the BC in this case). Is there another way I can get the Controller to listen to an event coming from a mock?

I'm also trying to think of a way to get the mock to raise the MessageRaised event, but that might be another question altogether.

Here is the code:

Business Component Interface

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

Controller

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

Unit Test

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  A: 

It seems the following code uses a mock object and, by using it, causes an expectation to be recorded:

Controller _controller = new Controller(_bc);

You are using the mock object like this:

_bc.MessageRaised += MessageWatch;

As a result, you have set up an expectation that an event handler is added to _bc.MessageRaised. This does not happen in the playback block, so an error is raised.

See also this question about when an object enters the record state. To be honest, I also don't understand why there is an explicit record syntax if objects enter the record state implicitly anyway.

Wim Coenen
Thank you! The other question helped out some since I didn't know about the BackToRecord() method.
Rorschach
A: 

I got it to work by combining a few things (not entirely sure how it works, but it does):

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

This made the test in the question work, as well as letting me raise an event from the Mock object in other tests, like:

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

Hope others find this useful!

Rorschach
A: 

Here's how I deal with raising events from a mock object:

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

In this case, port is a mock object with an event called DataPacketReceived.

Ideally, I always try to put my mock objects in playback mode before passing them to the system under test. This avoids any "unexpected expectations".

Don Kirkby