简体   繁体   English

Moq在构造函数中使用延迟加载

[英]Moq Using Lazy Loading in Constructor

I ran into a strange problem today - I was writing a unit test around a SIM object. 我今天遇到了一个奇怪的问题 - 我正在围绕SIM对象编写单元测试。 It asserts that when the SIM object is updated and the PIN attempts remaining has changed, it will call a particular method. 它声称当更新SIM对象并且PIN尝试剩余已更改时,它将调用特定方法。 The test looks like this: 测试看起来像这样:

[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());
}

So the mock SIM object is created with 10 PIN attempts remaining. 因此创建模拟SIM对象时剩余10次PIN尝试。 The SimPinInfo object then has the PinAttemptsRemaining value reduced to 2 before being passed to the UpdateSimInfo() method. 然后, SimPinInfo对象在传递给UpdateSimInfo()方法之前将PinAttemptsRemaining值减少为2。

SIM constructor (trimmed for clarity): SIM构造函数(为清晰起见修剪):

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

And the UpdateSimInfo() method (trimmed): UpdateSimInfo()方法(修剪):

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

A pretty simple test - what should happen is the above if statement will be true (pin attempts remaining has changed) so the OnPinAttemptsRemaining event will be fired. 一个非常简单的测试 - 应该发生的是上面的if语句是真的(引脚尝试剩余已经改变)所以将触发OnPinAttemptsRemaining事件。 However, the test failed (although not all the time - it passed when I slowly stepped through the code!). 但是,测试失败了(尽管不是所有的时间 - 当我慢慢地通过代码时它已经过去了!)。 What was happening was the if statement was false - both _pinAttemptsRemaining and info.PinAttemptsRemaining were 2. It appears that the SIM mock is not actually created when expected - when the info.PinAttemptsRemaining was 10. 发生的事情是if语句是假的 - _pinAttemptsRemaininginfo.PinAttemptsRemaining都是2.看来SIM模拟实际上并没有在预期时创建 - 当info.PinAttemptsRemaining为10时。

To demonstrated this, I added a comment: 为了证明这一点,我补充说:

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

I also put a breakpoint in the SIM object's constructor. 我还在SIM对象的构造函数中放置了一个断点。 The breakpoint was hit when stepping over the Console.WriteLine line, not the new Mock... line. 踩到Console.WriteLine行时,断点被击中,而不是new Mock...行。 So the object is not being created until needed. 因此,在需要之前不会创建对象。

I believe this is called lazy-loading or lazy-evaluation. 我相信这被称为延迟加载或懒惰评估。

There were various workaround to this behaviour - I ended up creating a new SimPinInfo object to pass to UpdateSimInfo() . 这种行为有各种解决方法 - 我最终创建了一个新的SimPinInfo对象以传递给UpdateSimInfo()

Has anyone come across this behaviour before? 有没有人遇到过这种行为? I couldn't find any references to it. 我找不到任何引用。

From what I've read you're trying to test a Mock. 从我读过的,你试图测试一个模拟。

sim.Object.UpdateSimInfo(info);

Mocks are meant to substitute dependencies, you don't use them to substitute the code you mean to test. 模拟意味着替换依赖项,您不使用它们来替换您要测试的代码。 Without seeing the whole code, I'd guess that you don't even need a Mock to test this behaviour. 没有看到整个代码,我猜你甚至不需要Mock来测试这种行为。 Assuming that FireOnPinAttemptsRemaining raises an event I'd recommend something like this for a test method: 假设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);
}

I'm not 100% on the event handler since I don't know the Fire method is raising an event or not, or the event name, but it should give you an idea. 我不是100%在事件处理程序,因为我不知道Fire方法是否引发事件,或事件名称,但它应该给你一个想法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM