2

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?

5
  • Consider using proper naming conventions. The names of your enumes are close to useless. in prefix is unneeded as out variables have a special designator. Commented Dec 11, 2012 at 8:32
  • why would they be close to useless? I don't understand, why would you suggest something that I think wouldn't even help me in my situation? Naming conventions are the least of my concerns; I named them as such because it's a requirement. Commented Dec 11, 2012 at 8:38
  • A name should convey a meaning. That's not the case with your names. My comment was meant to improve your overall coding. I am aware that this doesn't answer your question. That's actually the reason why I posted it as a comment and not as an answer. Commented Dec 11, 2012 at 8:43
  • Oh okay. Yes, I know the deal about proper naming conventions, I've been practicing ever since my college years. But let's just say I named my variable types and variables names that way because my boss says so. Those naming conventions that he wants have meaning and actually make sense to the project that I'm currently assigned to. Savvy? :) Commented Dec 11, 2012 at 8:49
  • Alright :-) (I wouldn't accept such naming conventions, no matter what the rational behind them) Commented Dec 11, 2012 at 8:54

1 Answer 1

3

You need to copy the Object reference from the serializable mock into a local variable so you can then pass it as ref.

IF_SerializableData localRef = serializable.Object;
target.PostEvent(..., ref localRef);

You can't pass ref Serializable.Object because 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 get and set accessor method(s) providing variable-like capabilities but, crucially, when you get a property you always get a copy of the underlying variable - even if that variable has a reference type.

So:

public class MyClass {
  private object _property;
  public object Property {
    get { return _property; } //<-- _property reference copied on to stack & returned
                              //    here as a new variable.  Therefore a ref
                              //    on that is effectively meaningless.
                              //    for a ref to be possible you'd need a pointer 
                              //    to the _property variable (in C++ terms)
    set { _property = value; }
  }
}

In this example - if you could pass ref MyClass.Property to 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 the Property get 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 ref method to appear on your Mock<T> you'd need to set it's Object property 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 Property in this way (see the aforementioned SO I linked to). To my mind it's similar to ref Derived not being compatible with ref 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 on ref or out parameters 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 ref at all (because that's just a memory location).

As a taster you'd have to write such a method something like this:

 public static void MyUglyRefMethod(Func<object> refGet, Action<object> refSet)
 {
   var read = refGet();
   var newValue = new object();
   refSet(newValue);
 }

With this, we can now provide ref like semantics over MyClass:

 MyClass a = new MyClass();
 MyUglyRefMethod(() => a.Property, (newValue) => a.Property = newValue);
 Assert.IsNotNull(a.Property);

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.

Sign up to request clarification or add additional context in comments.

2 Comments

OMG it works XDDD can you explain to me as to why this is necessary? and what is actually happening behind that code? thanks a million :)
I've put an explanation - not sure if it's better than anyone else's but I think it gets to the heart of the matter

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.