我测试了从更改后引发的事件检查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);
}
}
在CheckMockedClass
中number
实际上已更改,但仅在代码退出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);
答案 0 :(得分:0)
根据文件
必须让事件在具体类上虚拟化,因为这是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);
}
这实际上模仿了基本方法所做的事情,因此我担心模拟被测试的类。
这有一个代码味道,并建议检查正在测试的代码的当前设计。