CallTo在循环中不起作用

时间:2017-05-31 15:38:06

标签: fakeiteasy

我正在尝试创建一个在测试中使用的伪对象缓存类,我遇到了奇怪的行为。

我从这开始:

IObjectCache objectCache = A.Fake<IObjectCache>();

List<ICachedObject> objects = new List<ICachedObject>();

ICachedObject object1 = A.Fake<ICachedObject>();
A.CallTo(() => object1.Id).Returns(random.Next());
objects.Add(object1);

//Snip: Identical statements for object2 & object3...

A.CallTo(objectCache)
    .Where(call => call.Method.Name == "get_Item")
    //"get_Item" is the generated name for the indexer []
    .WithReturnType<ICachedObject>()
    .WhenArgumentsMatch((int objectId) => objectId.Equals(object1.Id))
    .Returns(object1);

//Snip: Identical statements for object2 & object3...

//The lookup works as expected:
var x = objectCache[object1.Id]; //returns object1
var y = objectCache[object2.Id]; //returns object2
var z = objectCache[object3.Id]; //returns object3

在开始工作之后,我想通过移动&#34; CallTo&#34;来清理测试。循环语句。但是,当我这样做时,假的不再返回预期的对象。

foreach (ICachedObject obj in objects)
{
    A.CallTo(objectCache)
        .Where(call => call.Method.Name == "get_Item")
        //"get_Item" is the generated name for the indexer []
        .WithReturnType<ICachedObject>()
        .WhenArgumentsMatch((int objectId) => objectId.Equals(obj.Id))
        .Returns(obj);
}

//Only the last object "added" works
var x = objectCache[object1.Id]; //returns a new, empty object
var y = objectCache[object2.Id]; //returns a new, empty object
var z = objectCache[object3.Id]; //returns object3

我想知道是否可能是WhenArgumentsMatch&amp;在索引器查找调用它们之前不会计算返回值,并且只能保存一个值。还有另一种方法吗?

1 个答案:

答案 0 :(得分:2)

我无法重现您的问题,但我有一种强烈的怀疑......您使用的是C#4还是更早?在C#5之前,foreach循环中的循环变量在逻辑上声明循环之外,所以如果你在lambda中捕获它,你总是引用相同的变量。这意味着当评估lambda (int objectId) => objectId.Equals(obj.Id)时,obj始终引用列表中的最后一项。此行为在C#5中已更改,因此循环变量在逻辑上声明循环中,这可以防止出现这种令人惊讶的行为(有关详细信息,请参阅Eric Lippert的this article)。

如果确实是您遇到问题的原因,请在循环中复制obj

foreach (ICachedObject obj in objects)
{
    var copy = obj;
    A.CallTo(objectCache)
        .Where(call => call.Method.Name == "get_Item")
        //"get_Item" is the generated name for the indexer []
        .WithReturnType<ICachedObject>()
        .WhenArgumentsMatch((int objectId) => objectId.Equals(copy.Id))
        .Returns(copy);
}

顺便说一句,您可以更轻松地配置这些调用:

foreach (var obj in objects)
{
    var copy = obj;
    A.CallTo(() => objectCache[copy.Id]).Returns(copy);
}