46

I have a presenter class, that attaches an event of the injected view. Now I would like to test the presenter reacting correctly to the event.

This is the view interface IView:

public interface IView 
{
    event EventHandler MyEvent;
    void UpdateView(string test);
}

This is the view implementing IView:

public partial class MyView : IView
{
    public event EventHandler MyEvent;

    public MyView()
    {
        this.combo.SelectedIndexChanged += this.OnSelectedIndexChanged;
    }

    public void UpdateView(string test)
    {
        this.textBox.Text = test;
    }

    private OnSelectedIndexChanged(Object sender, EventArgs e)
    {
        if (this.MyEvent != null)
        {
            this.MyEvent(sender, e);
        }
    }
}

This is the presenter under test:

public class MyPresenter
{
    private IView _view;
    public MyPresenter(IView view)
    {
        this._view = view;
        this._view.MyEvent += this.OnMyEvent;
    }

    private void OnMyEvent(Object sender, EventArgs e)
    {
        this._view.UpdateView();
    }
}

This is the test fixture testing MyPresenter:

[TestClass]
public class MyPresenterFixture()
{
    private MyPresenter testee;
    private Mock<IView> mockView;

    [TestMethod]
    public void ShouldReactOnMyEvent()
    {
        // arrange
        this.mockView = new Mock<IView>(MockBehavior.Strict);
        this.testee = new MyPresenter(this.mockView.Object);

        // act
        this.mockView.Raise(mock => mock.MyEvent += null); // this does not fire

        // assert and verify
        this.mockView.Verify(mock => mock.UpdateView(It.IsAny<string>());
    }
}

I am using Moq 4. Is it possible to do what I want?

Anton Menshov
  • 2,266
  • 14
  • 34
  • 55
Yannik
  • 709
  • 1
  • 7
  • 11

2 Answers2

93

Don't you need to pass the argument? Your event signature is EventHandler, which is
(object sender, EventArgs e).

this.mockView.Raise(mock => mock.MyEvent += null, new EventArgs());

I've never used the overload you've specified here... it doesn't seem correct, though.

Pang
  • 9,564
  • 146
  • 81
  • 122
Anderson Imes
  • 25,500
  • 4
  • 67
  • 82
  • 4
    +1. Or pass in EventArgs.Empty. You need to pass any parameters apart from the sender as arguments to Mock.Raise. Links http://madcoderspeak.blogspot.com/2010/01/meet-frameworks-rhino-v-moq-v-jmock.html#scen8 http://code.google.com/p/moq/wiki/QuickStart – Gishu May 19 '11 at 08:15
  • 1
    Why we are using mock.MyEvent += null ?? please explain as I am a noob. – Bhoopathi Reddy Mar 02 '21 at 08:51
  • 1
    @BhoopathiReddy : an event can only appear on the left hand side of += or -=. See [CS0079](https://learn.microsoft.com/en-us/dotnet/csharp/misc/cs0079?f1url=%3FappId%3Droslyn%26k%3Dk(CS0079)) – RuudSieb Jul 09 '21 at 14:03
3

You've declared UpdateView() as accepting a string, but your presenter invocation does not have a string argument (or default):

Invocation:

private void OnMyEvent(Object sender, EventArgs e)
{
    this._view.UpdateView();
}

Declaration:

public void UpdateView(string test)
{
    this.textBox.Text = test;
}

Verification:

mockView.Verify(mock => mock.UpdateView(It.IsAny<string>());

FWIW, I think the event in your view is a bit cumbersome, a simple change would be to:

public interface IView
{
    void UpdateText(string test);
}

public class MyPresenter
{
    private readonly IView _view;
    public MyPresenter(IView view)
    {
        _view = view;
    }

    private void SelectItem(string item)
    {
        _view.UpdateText(item);
    }
}

public partial class MyView : IView
{
    private readonly MyPresenter _presenter;

    public MyView()
    {
        _presenter = new MyPresenter(this);
        combo.SelectedIndexChanged += OnSelectedIndexChanged;
    }

    public void UpdateText(string test)
    {
        textBox.Text = test;
    }

    private OnSelectedIndexChanged(Object sender, EventArgs e)
    {
        _presenter.SelectItem(combo.SelectedItem);
    }
}

Then you could just verify the interaction with the view without having an additional event to deal with:

    presenter.SelectItem("Burrito!");

    mockView.Verify(mock => mock.UpdateView("Burrito!");
Ritch Melton
  • 11,498
  • 4
  • 41
  • 54
  • Hi Ritch, thanks for your feedback. I have seen that in the presenters method private void OnMyEvent(Object sender, EventArgs e) { this._view.UpdateView(); } the input for UpdateView is missing. Unfortunately your approach does not work for us, because we implement pure passive view and the view does never know its presenter. Cheers – Yannik May 16 '11 at 15:52