Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# and Moq, raise event declared in interface from abstract class mock

I am writing the unit test and receive exception when trying to raise the event from abstract class mock. Here is the sample code:

    public abstract class AbstractBase : EntityObject
    {}

    [TestMethod]
    public void MyTest()
    {

        var mock = new Mock<AbstractBase>();
        var notificationMock = entityMock.As<INotifyPropertyChanged>();

        var propertyChangedMapper = new PropertyChangedMapper();

        bool eventReceived = false;
        propertyChangedMapper.MyPropertyChanged +=
            (sender, eventArgs) =>
                {
                    eventReceived = true;
                };

        propertyChangedMapper.Subscribe((AbstractBase)notificationMock.Object);

        Assert.IsFalse(eventReceived);

        notificationMock.Raise(e=>e.PropertyChanged += null, 
                                   new PropertyChangedEventArgs("Property1"));

        Assert.IsTrue(eventReceived);
  }

Obviously I could use mock on INotifyPropertyChanged and event is risen just fine, but inside of PropertyChangedMapper I need to cast the sender to AbstractBase which fails if using Mock<INotifyPropertyChanged>

EDIT: As per suggestion using Mock.As<>() seems to be the right way to go, the only problem above is that the event risen from notificationMock has nothing to do with the original mock of the object. Code:

        notificationMock.Object.PropertyChanged += (s, e) =>
        {
            var result = "this one is fired as it should";
        };

        mock.Object.PropertyChanged += (s, e) =>
        {
            var result = "this one is not called but is actually what I need";
        }; 

        notificationMock.Raise(e => e.PropertyChanged += null, 
                 new PropertyChangedEventArgs("Property1"));
like image 398
Paul Avatar asked Sep 19 '11 16:09

Paul


3 Answers

You may be able to do the desired cast if you make your mock a mult-mock. Since Moq mocks are tied to an individual type via the generic argument, you must explicitly progressively add additional interfaces or super-classes to the mock, and then use the end product in your test. A quick example of how to do this is below.

var baseMock = new Mock<AbstractBase>();
var inpcMock = baseMock.As<INotifyPropertyChanged>();

// ...setup event...

propertyChangedMapper.Subscribe(inpcMock.Object);

// ... assertions ...
like image 98
Chris Ammerman Avatar answered Sep 21 '22 18:09

Chris Ammerman


Given the way you're doing this, there is no implementation of the event. The interface itself is just the contract that says "I have a PropertyChanged event." If you want to raise that event, you have to provide a handler, even if it doesn't do anything. Implement the PropertyChanged event in your mocked class to raise the event.

UPDATE:

Try this code for your AbstractBase:

public abstract class AbstractBase : INotifyPropertyChanged
{
     public virtual event PropertyChangedEventHandler PropertyChanged;
     private void NotifyPropertyChanged(String info)
     {
         if (PropertyChanged != null)
         {
            PropertyChanged(this, new PropertyChangedEventArgs(info));
         }
     }
}
like image 25
Dave Swersky Avatar answered Sep 23 '22 18:09

Dave Swersky


Is your PropertyChanged event declared as a virtual event?

public abstract class AbstractBase : INotifyPropertyChanged
{
   public virtual event PropertyChangedEventHandler PropertyChanged;
}

(See also: Jon Skeet on virtual events.)

like image 45
ladenedge Avatar answered Sep 21 '22 18:09

ladenedge