Moq - 虚拟方法中的引用参数在类被模拟时无法正确更新

时间:2018-02-06 15:32:14

标签: c# unit-testing moq

我测试了从更改后引发的事件检查ref参数值。当我创建真正的类时,测试工作正常,但是当我尝试模拟类失败时。简单的代码示例:

[TestClass]
public class Fixture
{
    [TestMethod]
    public void CheckRealClass()
    {
        // this test passed
        var classToTest = new NumberChangerClass();
        CheckClass(classToTest);
    }

    [TestMethod]
    public void CheckMockedClass()
    {
        // this test failed
        var classToTest = new Mock<NumberChangerClass>() { CallBase = true };
        CheckClass(classToTest.Object);
    }

    private void CheckClass(NumberChangerClass numberChangerClass)
    {
        // Arrange
        var number = 1;
        numberChangerClass.StorageChanged += (sender, args) =>
        {
            Assert.AreEqual(2, number);
        };


        // Act
        numberChangerClass.SetValue(ref number, 2);
    }
}

public class NumberChangerClass
{
    public event EventHandler StorageChanged;

    public virtual void SetValue(ref int storage, int newValue)
    {
        storage = newValue;
        StorageChanged?.Invoke(this, null);
    }
}

CheckMockedClassnumber实际上已更改,但仅在代码退出SetValue方法之后。

我使用Moq版本4.2.1409.1722,但它在最新版本(4.8.1)中也不起作用。

有一种方法可以让CheckMockedClass通过吗?

更新

我在moq github中发布了问题:link

简短回答:没有什么可以做的。

更多详情:

  

您正在观察的行为是DynamicProxy(Moq依赖)如何在方法调用拦截期间处理by-reference参数(在https://github.com/castleproject/Core/blob/master/docs/dynamicproxy-by-ref-parameters.md中记录)的直接结果:参数在单独的位置缓冲在一个单独的位置调用拦截并仅在拦截结束时写回,这就是为什么参数以延迟/延迟方式更新的原因。遗憾的是,没有什么可以做的。

当我检查Assert.AreEqual(2, number);

时,我仍然试图找到获取更新号码的方法

1 个答案:

答案 0 :(得分:0)

根据文件

参考Moq Quickstart

必须让事件在具体类上虚拟化,因为这是Moq可以使用的。如果在接口上定义它,它将开箱即用。

public class NumberChangerClass {
    public virtual event EventHandler StorageChanged;

    public virtual void SetValue(ref int storage, int newValue) {
        storage = newValue;
        StorageChanged?.Invoke(this, null);
    }
}

为了模仿它并举起活动。

delegate void SetValueCallback(ref int storage, int newValue);

[TestMethod]
public void CheckMockedClass() {
    var classToTest = new Mock<NumberChangerClass>() { CallBase = true };
    // callbacks for methods with `ref` / `out` parameters are 
    //possible but require some work (and Moq 4.8 or later):

    classToTest
        .Setup(_ => _.SetValue(ref It.Ref<int>.IsAny, It.IsAny<int>()))
        .Callback(new SetValueCallback((ref int storage, int newValue) => {
            storage = newValue;
            classToTest.Raise(_ => _.StorageChanged += null, EventArgs.Empty);
        }));
    CheckClass(classToTest.Object);
}

这实际上模仿了基本方法所做的事情,因此我担心模拟被测试的类。

这有一个代码味道,并建议检查正在测试的代码的当前设计。