I’ve asked a question before on how to set value of interface in testing a method
. I’ve successfully implemented the Moq framework into my project and the test runs fine.
this is the sample code that I’ve featured:
public void PostEvent(
eVtCompId inSenderComponentId,
eVtEvtId inEventId,
long inEventReference,
IF_SerializableData inEventData)
{
if(mEventMap.ContainsKey(inEventId))
{
mEventMap[inEventId](inSenderComponentId, inEventReference, inEventData);
}
}
Here I have 4 parameters: 1st: an enum, 2nd: another enum, 3rd: long, 4th: an interface. However, I was mistaken in that the 4th parameter (the interface), isn’t supposed to be an interface, but rather a reference to the interface.
so it should look like this:
public void PostEvent(
eVtCompId inSenderComponentId,
eVtEvtId inEventId,
long inEventReference,
ref IF_SerializableData inEventData)
the sample Moq test code that was given to me (which is this)…
var serializable = new Mock<IF_SerializableData>();
target.PostEvent(..., serializable.Object);
…doesn’t work. I’ve tried ref serializable.Object but it still doesn’t work because I get an error that says the ref parameter is expecting a reference to a variable, not an object.
Any tips or examples on how to properly test this?
You need to copy the
Objectreference from theserializablemock into a local variable so you can then pass it asref.You can’t pass
ref Serializable.Objectbecause its a property – see also Is it possible to pass properties as "out" or "ref" parameters? which offers an excellent discussion of why this is the case, and a source of other links.My explanation
This is ultimately because properties are not variables. A read/write property is a pair
getandsetaccessor method(s) providing variable-like capabilities but, crucially, when yougeta property you always get a copy of the underlying variable – even if that variable has a reference type.So:
In this example – if you could pass
ref MyClass.Propertyto a method – it would be meaningless because it would be passing a reference to a transient variable on the stack – i.e. the copy of the reference returned by thePropertyget accessor; it would not be passing the property by reference. So C# doesn't allow it, even though it could, because it would imply thatProperty` can be modified by the method – when it simply can’t.Hence why we need to capture that value from the stack and copy it into a local variable in your case. Now – note that in order for the new value set by the
refmethod to appear on yourMock<T>you’d need to set it’sObjectproperty to the local variable value again (if you can – I don’t use Moq but I assume it’s Mocks are immutable).A lot of debate has been had as to whether C# should automatically handle
ref Propertyin this way (see the aforementioned SO I linked to). To my mind it’s similar toref Derivednot being compatible withref Base– yes there is a way that the language can handle this automatically for you, but should it? In my mind, no. Do I get frustrated by it? Oh yes of course – but often I find it highlights architectural weaknesses which really should be fixed (for example, relying onreforoutparameters where a return value is likely better).The only way for C# to allow you to pass a property by reference would be to pass the get and set accessors to the target method – and this would not be compatible with
refat all (because that’s just a memory location).As a taster you’d have to write such a method something like this:
With this, we can now provide
reflike semantics overMyClass:But that is simply ugly as hell.
It’s simpler to make a method to take a
ref MyClass– and then it can write to any of the properties directly.