我正在尝试学习使用Moq(4)来测试下面的C#代码。我希望通过设置
mockTimer.SetupSet(m => m.Interval = It.IsInRange(
interval - shorter, interval + longer, Range.Inclusive));
在调用为interval属性赋值的方法(ageService.AddParticipant方法)之前,测试将在分配不正确的值时抛出。但是,即使将指定范围之外的值分配给AddParticipant方法中的Interval属性,测试也会通过。我还发现timer.Interval在执行AddParticipant方法后的值为0,即使我在步进AddParticipant方法时可以看到此属性被赋予非零值。
[TestMethod]
public void TestAgeUpdatingStartOnAdd()
{
var mockTimer = new Mock<IDispatcherTimer>();
mockTimer.SetupProperty(m => m.Interval);
mockTimer.Setup(m => m.Start());
mockTimer.Setup(m => m.Stop());
IDispatcherTimer timer = mockTimer.Object;
var ageService = new AgeUpdatingService(timer);
TimeSpan interval = TimeSpan.FromMinutes(10);
TimeSpan tolerance = TimeSpan.FromMilliseconds(100)
mockTimer.SetupSet(m => m.Interval = It.IsInRange(
interval - tolerance, interval + tolerance, Range.Inclusive));
ageService.AddParticipant(new Participant{ DOB = DateTime.Now + interval });
我做错了什么?我是否一起错过了SetupSet方法的要点(因此在执行函数之后应该坚持检查定时器对象的属性,这似乎在将SetupSet方法放入代码之前有效)?如果是这样,你能否解释一下SetupSet的存在。谢谢。
答案 0 :(得分:4)
由于您使用new Mock<IDispatcherTimer>()
创建模拟,因此默认为Loose
模拟行为。这意味着当模拟对象以未通过Setup
方法指定的方式使用时,它不会抱怨。使用接受MockBehavior
枚举并指定Strict
的构造函数重载将使代码行为与您期望的一样。看起来这就是你期望用Setup
次电话判断它的方式;如果你使用松散的嘲笑,它们将是不必要的。
或者,您可以保留松散的模拟,并在预期设置后将SetupSet
的{{1}}更改为Interval
。即:
VerifySet
这就像一个测试断言,意味着如果在调用它时一组var ageService = new AgeUpdatingService(timer);
TimeSpan interval = TimeSpan.FromMinutes(10);
TimeSpan tolerance = TimeSpan.FromMilliseconds(100)
ageService.AddParticipant(new Participant{ DOB = DateTime.Now + interval });
mockTimer.VerifySet(m => m.Interval = It.IsInRange(
interval - tolerance, interval + tolerance, Range.Inclusive));
属性从未发生过,它将抛出一个Interval
并且无法通过测试。
答案 1 :(得分:1)
SetupSet
可用于设置属性设置器 - 需要这样做是相当不寻常的,因为传递给Set的参数通常不需要捕获,因为可以使用以下方法验证对Setter的调用在“行动”步骤之后VerifySet
。
这是实现目标的方法:
var mockTimer = new Mock<ITimer>();
// Simulate the actions of the Sut with the Mock
mockTimer.Object.Interval = 6;
mockTimer.Object.Interval = 7;
mockTimer.Object.Interval = 999;
// Ensure the mock was called in-band
mockTimer.VerifySet(m => m.Interval = It.IsInRange(5, 10, Range.Inclusive),
Times.Exactly(2));
// Ensure the mock was not called out of band
mockTimer.VerifySet(m => m.Interval = It.Is<int>(i => i < 5 || i > 10),
Times.Never);
另一种不太优雅的方法是使用SetupSet
直接检测无效的调用:
mockTimer.SetupSet(m => m.Interval = It.Is<int>(i => i < 5 || i > 10))
.Throws(new ArgumentOutOfRangeException());