2017-05-31 2 views
1

Ich versuche, eine Fälschung einer Objekt-Cache-Klasse zu erstellen, um in einem Test zu verwenden, und ich renne in seltsames Verhalten.CallTo funktioniert nicht in einer Schleife

ich damit begonnen: Der Test ein wenig aufzuräumen, indem die „callto“ -Aussagen zu einer Schleife

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 

Nach dass die Arbeit bekommen, die ich wollte. Wenn ich das mache, gibt die Fälschung jedoch nicht mehr die erwarteten Objekte zurück.

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 

Ich frage mich, ob vielleicht die Argumente zu WhenArgumentsMatch & Returns nicht ausgewertet werden, bis sie durch den Indexer Lookup genannt werden und nur ein Wert halten kann. Gibt es einen anderen Weg, dies zu tun?

Antwort

2

Ich kann Ihr Problem nicht reproduzieren, aber ich habe einen starken Verdacht ... Verwenden Sie C# 4 oder früher? Vor C# 5 wurde die Schleifenvariable in einer foreach-Schleife logisch außerhalb der Schleife deklariert. Wenn Sie sie also in einem Lambda erfassten, bezogen Sie sich immer auf die gleiche Variable. Das heißt, wenn Ihr Lambda (int objectId) => objectId.Equals(obj.Id) ausgewertet wird, bezieht sich obj immer auf den letzten Eintrag aus der Liste. Dieses Verhalten wurde in C# 5 geändert, so dass die Schleifenvariable logisch innerhalb der Schleife ist, die dieses überraschende Verhalten verhindert (siehe this article von Eric Lippert für Details).

Wenn dies tatsächlich der Grund für Ihr Problem, macht nur eine Kopie von obj in der Schleife:

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

By the way, können Sie diese Anrufe wesentlich einfacher konfigurieren:

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

Geschafft! Das war genau das Problem. –

+0

... Oder sogar 'objectCache [copy.Id] = copy;', wenn es einen Setter für den Indexer gibt. –

+0

@BlairConrad tatsächlich, aber ich nahm an, dass der Indexer schreibgeschützt war. –

Verwandte Themen