当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中(它在我的实际代码中发生的方式)。是否有不同的方式来提升可行的事件,或者以其他方式解决这个问题?我希望能够测试需要弱订阅的对象的事件处理。
答案 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"));