2

Given this code:

[ContractClass(typeof(DogContract))]
public interface IDog {
  void Eat(object obj);
}

[ContractClassFor(typeof(IDog))]
internal abstract DogContract : IDog {
  public void Eat(object obj) {
    Contract.Requires<ArgumentNullException>(obj != null);
  }
}

var dogMock = new Mock<IDog>();
dogMock.Object.Eat(null); // Throws ArgumentNullException

It seems like the rewriter is somehow putting its behavior into the mocked object, which I didn't really expect. I don't think its a real problem, just unexpected. Anyone know how this is happening?

Andy
  • 8,432
  • 6
  • 38
  • 76
  • possible duplicate of [Moq and Code Contracts](http://stackoverflow.com/questions/6390348/moq-and-code-contracts) – TrueWill Jan 09 '12 at 17:42
  • 1
    It's not clear to me. When you call dogMock.Eat(null), it should try to execute this, so it's expected behaviour. – AD.Net Jan 09 '12 at 17:42
  • Are you saying that `Contract.Requires(obj != null)` should not run when using Moqs? – Austin Salonen Jan 09 '12 at 17:51
  • @AD.Net I don't see how its necessarly clear. I wasn't, nor did anyone on my team, expect the rewriter to enforce this. That's my question, HOW did the rewriter know to get involved for this, since Mock likely isn't compiled until runtime (by Moq). – Andy Jan 09 '12 at 17:57
  • @AustinSalonen, no, I think its fine. I'm just not clear where the rewriter is seeing that it should get involved. What does it see that it actually picks up on this is the question. – Andy Jan 09 '12 at 17:58
  • @TrueWill, not a duplicate. The code in that question is just wrong, and he's asking how to fix it. At least, that's my interpretation. Also, I'm asking HOW the rewriter gets involved, I'm not asking if it does. It does, clearly, if you execute the code. – Andy Jan 09 '12 at 17:59
  • @Andy - OK - apparently I can't undo my "possible duplicate." As long as others don't vote to close then the question is fine. – TrueWill Jan 09 '12 at 18:10
  • Are you sure? I just used your exact code and the precondition isn't hit. – porges Jan 11 '12 at 10:33
  • Did you turn on the checking in the project properties? Assembly mode = Standard Validation Requires, Preform Runtime Contract Checking enabled and Full. Call-site requires checking enabled. Build reference assemblies is also set. – Andy Jan 11 '12 at 19:19

1 Answers1

1

"Call-site Requires checking" will do it. The rewriter will then put the preconditions into the caller code and not the implementations. So, even though the code in the mocked object can't have been rewritten (it's generated at runtime), the code in the caller can be.

Here's what the generated code looks like without call-site Requires:

private static void Main(string[] args)
{
    Mock<IDog> m = new Mock<IDog>();
    m.Object.Eat(null);
}

And with:

private static void Main(string[] args)
{
    Mock<IDog> m = new Mock<IDog>();
    IDog.V$Eat(m.Object, null);
}

IDog is a static class that contains all the methods from the IDog interface, along with the preconditions. Here is what Eat looks like:

internal static void V$Eat(IDog @this, object obj)
{
    __ContractsRuntime.Requires<ArgumentNullException>(
                      obj != null, null, "obj != null");
    @this.Eat(obj);
}

So this way, the preconditions will be called even if the code in the class can't have been rewritten.

porges
  • 30,133
  • 4
  • 83
  • 114