0

我今天遇到了一个奇怪的问题——我正在围绕一个 SIM 对象编写一个单元测试。它断言当 SIM 对象更新并且剩余的 PIN 尝试已更改时,它将调用特定方法。测试看起来像这样:

[Test]
public void TestUpdateSimInfoWithPinAttemptsChangedCallsOnPinAttemptsRemaining()
{
    var info = new SimPinInfo {PinAttemptsRemaining = 10};

    var sim = new Mock<Sim>(info);

    info.PinAttemptsRemaining = 2;

    sim.Object.UpdateSimInfo(info);

    sim.Verify(s => s.FireOnPinAttemptsRemaining(), Times.Once());
}

因此,创建模拟 SIM 对象时剩余 10 次 PIN 尝试。然后在传递给方法之前,该SimPinInfo对象的值减少到 2 。PinAttemptsRemainingUpdateSimInfo()

SIM 构造函数(为清楚起见进行了修剪):

internal Sim(SimPinInfo info) : this()
{
     _pinAttemptsRemaining = info.PinAttemptsRemaining;
     _pukAttemptsRemaining = info.PukAttemptsRemaining;
     ......
}

UpdateSimInfo()方法(修剪):

internal void UpdateSimInfo(SimPinInfo info)
{
    lock(_locker)
    {
        if (_pinAttemptsRemaining != info.PinAttemptsRemaining)
        {
            Log("PinAttemptsRemaining changed");
            _pinAttemptsRemaining = info.PinAttemptsRemaining;
            FireOnPinAttemptsRemaining();
        }
        .....
    }
}

一个非常简单的测试 - 应该发生的是上面的 if 语句将为真(剩余的 pin 尝试已更改),因此OnPinAttemptsRemaining将触发事件。然而,测试失败了(虽然不是一直 - 当我慢慢地通过代码时它通过了!)。发生的事情是 if 语句是错误的 - 两者_pinAttemptsRemaining都是info.PinAttemptsRemaining2。看起来 SIM 模拟实际上没有按预期创建 - 当info.PinAttemptsRemaining是 10 时。

为了证明这一点,我添加了一条评论:

var sim = new Mock<Sim>(info);
info.PinAttemptsRemaining = 2;
Console.WriteLine("SIM's pin attempts = " + sim.Object.PinAttemptsRemaining);

我还在 SIM 对象的构造函数中放置了一个断点。跨过线时断点被击中Console.WriteLine,而不是new Mock...线。所以在需要之前不会创建对象。

我相信这被称为延迟加载或延迟评估。

这种行为有多种解决方法——我最终创建了一个新SimPinInfo对象来传递给UpdateSimInfo().

有没有人遇到过这种行为?我找不到任何对它的引用。

4

1 回答 1

2

从我读到的内容来看,您正在尝试测试 Mock。

sim.Object.UpdateSimInfo(info);

模拟旨在替换依赖项,您不会使用它们来替换您要测试的代码。如果没有看到整个代码,我猜你甚至不需要 Mock 来测试这种行为。假设 FireOnPinAttemptsRemaining 引发了一个事件,我推荐这样的测试方法:

[Test]
public void TestUpdateSimInfoWithPinAttemptsChangedCallsOnPinAttemptsRemaining()
{
    int eventFiredCount = 0;
    var info = new SimPinInfo {PinAttemptsRemaining = 10};
    var sim = Sim(info);
    sim.OnPinAttemptsRemaining += (sender, e) => { eventFiredCount++; };

    info.PinAttemptsRemaining = 2;
    sim.UpdateSimInfo(info);

    Assert.AreEqual(1, eventFiredCount);
}

我不是 100% 使用事件处理程序,因为我不知道 Fire 方法是否引发事件或事件名称,但它应该给你一个想法。

于 2012-11-01T22:04:47.237 回答