PropertyChangedEventManager和Moq&界面

时间:2017-03-30 21:04:39

标签: c# moq inotifypropertychanged

当Mocking(使用Moq)支持INotifyPropertyChanged的接口/对象时,使用PropertyChangedEventManager.AddHandler不会将属性连接到由Raise引发的事件。直接+ =连接正常工作:

bool _EventMangerCalled = false;
bool _DirectCalled = false;

private void EventManagerHandler(object sender, PropertyChangedEventArgs e)
{
    _EventMangerCalled = true;
}

private void DirectHandler(object sender, PropertyChangedEventArgs e)
{
    _DirectCalled = true;
}

[TestMethod]
public void PropertyChangedEventManager_AddHandler_OnMoq()
{
    var m = new Mock<INotifyPropertyChanged>();
    m.Object.PropertyChanged += DirectHandler;
    PropertyChangedEventManager.AddHandler(m.Object, EventManagerHandler, "Foo");

    m.Raise(x=>x.PropertyChanged += null, this, new PropertyChangedEventArgs("Foo"));
    // Success
    Assert.IsTrue(_DirectCalled, "directCalled is not true");
    // Failure
    Assert.IsTrue(_EventMangerCalled, "eventManagerCalled is not true"); 
}

我也尝试过string.Empty而不是&#34; Foo&#34;在AddHandler中(它在我的实际代码中发生的方式)。是否有不同的方式来提升可行的事件,或者以其他方式解决这个问题?我希望能够测试需要弱订阅的对象的事件处理。

2 个答案:

答案 0 :(得分:1)

首先,想想你的第一个断言:

var mock = new Mock<INotifyPropertyChanged>();
mock.Object.PropertyChanged += DirectHandler;
mock.Raise(x=>x.PropertyChanged += null, this, new PropertyChangedEventArgs("Foo"));
Assert.IsTrue(_DirectCalled, "directCalled is not true");

您验证当您在模拟对象上引发事件时,将调用事件处理程序(附加到模拟对象的事件)。换句话说,您正在测试Moq库的实现。别担心,它已经过测试。

接下来,要引发事件,您不需要将引用传递给定义了测试的类(this) - 只需传递应传递给处理程序的EventArgs:

.Raise(m => m.PropertyChanged += null, new PropertyChangedEventArgs("Foo"))

很难说出你的PropertyChangedEventManager应该有什么行为,但我认为如果源对象为给定属性引发了NotifyPropertyChanged事件,它应该运行给定处理程序。听起来不错。使用clojures测试处理程序调用很容易 - 使用lambda委托捕获局部变量(flag)并在调用lambda时更改它:

[TestMethod]
public void ShouldInvokeHandlerWhenSpecifiedPropertyChanged()
{
    var sourceMock = new Mock<INotifyPropertyChanged>();
    var sut = new PropertyChangedEventManager();
    bool handlerInvoked = false;
    sut.AddHandler(sourceMock.Object, (s, e) => handlerInvoked = true, "Foo");

    sourceMock.Raise(m => m.PropertyChanged += null, new PropertyChangedEventArgs("Foo"));

    Assert.IsTrue(handlerInvoked);
}

使这个测试通过的最简单的实现将是

public class PropertyChangedEventManager
{
    public void AddHandler(
        INotifyPropertyChanged source,
        EventHandler<PropertyChangedEventArgs> handler,
        string propertyName)
    {
        source.PropertyChanged += (s, e) => {
            if (e.PropertyName == propertyName)
                handler(s, e);
        };
    }
}

接下来,如果其他属性的source raise事件,则应检查是否未调用该处理程序。改变两行:

[TestMethod]
public void ShouldNotInvokeHandlerWhenOtherPropertyChanged()
{
    var sourceMock = new Mock<INotifyPropertyChanged>();
    var sut = new PropertyChangedEventManager();
    bool handlerInvoked = false;
    sut.AddHandler(sourceMock.Object, (s, e) => handlerInvoked = true, "Foo");

    sourceMock.Raise(m => m.PropertyChanged += null, new PropertyChangedEventArgs("Bar"));

    Assert.IsFalse(handlerInvoked);
}

答案 1 :(得分:0)

我想通了......对于直接案例,永远不会检查“发件人”。但是PropertyChangedManager将它用作订阅的密钥。我的:

m.Raise(x=>x.PropertyChanged += null, this, new PropertyChangedEventArgs("Foo"));

应该是:

m.Raise(x=>x.PropertyChanged += null, m.Object, new PropertyChangedEventArgs("Foo"));