我在名为MouseWheelListenerViewManager
的类中有以下方法:
protected static void PreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
if (!e.Handled)
{
e.Handled = true;
var eventArg = new MouseWheelEventArgs(e.MouseDevice, e.Timestamp, e.Delta);
eventArg.RoutedEvent = UIElement.MouseWheelEvent;
eventArg.Source = sender;
UIElement ancestor = ((FrameworkElement)sender).Parent as UIElement;
while (ancestor != null)
{
ancestor.RaiseEvent(eventArg);
ancestor = VisualTreeHelper.GetParent(ancestor) as UIElement;
}
}
}
我想测试.Parent
sender
被调用(以及所有其他祖先)。
我写了这个单元测试:
[Test]
public void PreviewMouseWheel_is_sent_to_ancestors()
{
TestableMouseWheelViewManager mouseWheelListenerViewManager = new TestableMouseWheelViewManager();
BorderedCanvas canvas = mouseWheelListenerViewManager.CreateView(new ThemedReactContext(new ReactContext()));
mouseWheelListenerViewManager.AddListeners(canvas);
MouseDevice mouseDev = InputManager.Current.PrimaryMouseDevice;
Mock<MouseWheelEventArgs> me = new Mock<MouseWheelEventArgs>(mouseDev, 0, 0);
me.Object.RoutedEvent = UIElement.MouseWheelEvent;
///start of most relevant code
Mock<FrameworkElement> sender = new Mock<FrameworkElement>();
Mock<StackPanel> parent = new Mock<StackPanel>();
Mock<StackPanel> grandParent = new Mock<StackPanel>();
parent.Object.Children.Add(sender.Object);
grandParent.Object.Children.Add(parent.Object);
TestableMouseWheelViewManager.PreviewMouseWheel(sender, me.Object);
///end of most relevant code
parent.Verify(mock => mock.RaiseEvent(It.IsAny<RoutedEventArgs>()), Times.Once());
grandParent.Verify(mock => mock.RaiseEvent(It.IsAny<RoutedEventArgs>()), Times.Once());
}
但是,测试代码会抛出此异常:
System.NullReferenceException : Object reference not set to an instance of an object.
在这一行:
parent.Object.Children.Add(sender.Object);
我没有设置Children
,而是试图设置Parent
,如下所示:
sender.Parent = parent;
但是,这是不可能的,因为Parent
的{{1}}是只读的。
那么如何模拟FrameworkElement
继承链?
答案 0 :(得分:0)
Moq只会在您尝试模拟具体课程时给您带来痛苦,正如您在此处所做的那样(FrameworkElement
),正如您所发现的那样。
相反,您应该考虑使用具体FrameworkElement
对象的实例编写测试。值得庆幸的是,实例化FrameworkElement
并不需要大量依赖。
为了验证调用FrameworkElement.RaiseEvent()
方法,您可以使用处理程序订阅每个FrameworkElement.MouseWheel
事件,该处理程序将标记它已被调用。
FrameworkElement sender = new FrameworkElement();
FrameworkElement parent = new FrameworkElement();
FrameworkElement grandParent = new FrameworkElement();
parent.Children.Add(sender);
grandParent.Children.Add(parent);
Dictionary<FrameworkElement, bool> callFlags = {
{sender, False},
{parent, False},
{grandParent, False}
};
var handler = (sndr, e) => { callFlags[(sndr as FrameworkElement)] = True };
sender.MouseWheel += handler;
parent.MouseWheel += handler;
grandParent.MouseWheel += handler;
/* run tests */
// assert all call flags have been set to true
Assert.IsTrue(callFlags.All(cf => cf), "A MouseWheel event in the hierarchy was not raised");
(我目前无法测试此代码,所以希望它足以提供一个想法)
同样,您可能无法将Moq与具体的FrameworkElement
对象一起使用。我将简单解释为什么你收到你报告的错误。
在Moq中,new Mock<T>(MockBehavior)
采用MockBehavior
参数。 This a really good source on what this does。该参数是可选。
要点是,因为您调用Mock<T>
的默认构造函数,所传递的行为是MockBehavior.Default
。这意味着,在没有特定setup()
的情况下,当您调用Mock<T>.Object
的阴影成员时,Moq将为其各自的类型实例化默认值,例如如果您呼叫int
成员,则Moq返回0
,bool
成员返回False
,复杂类型成员返回null
。
现在你的代码(摘录):
Mock<FrameworkElement> sender = new Mock<FrameworkElement>();
Mock<StackPanel> parent = new Mock<StackPanel>();
parent.Object.Children.Add(sender.Object);
由于parent
已使用MockBehavior.Default
进行了实例化,并且由于您从未调用parent.Setup(p => p.Children).Returns(...)
,因此您获得了:
parent.Object.Children == null
// True
请注意,在Moq中,当您模拟某个类型时,您需要评估不同MockBehavior
值的优点,并调用Setup()
或保留行为以决定每个成员将在您的测试方法中调用。