I have some interfaces which I'm trying to mock with the following structure (simplified):
interface A
{
DateTime DateCreated { get; set; }
}
interface B : A
{
DateTime DateCreated { get; set; }
}
The problem I'm having is that DateCreated is a separate property for each interface, so even though the concrete objects that I'm aware of only have one shared implementation of these shared properties, calling Mock.SetupAllProperties gives each one a separate implementation. This means they do not share values, so calling ((B)obj).DateCreated = {blah} doesn't give the desired result elsewhere when ((A)obj).DateCreated is accessed.
The only way I see to solve this in Moq is to do the following:
var m = new Mock<B>();
DateTime closure;
m.SetupGet(x => x.DateCreated).Returns(() => closure);
m.SetupSet(x => x.DateCreated).Callback(value => { closure = value; });
m.As<A>.SetupGet(x => x.DateCreated).Returns(() => closure);
m.As<A>.SetupSet(x => x.DateCreated).Callback(value => { closure = value; });
This is tedious, error prone and I need to do this for at least a dozen properties and I don't know how many more. I could probably write a generic method to do this but it seems like there must be a simpler solution. Can anyone suggest a better way to do this?
(I'd love to "correct" the definitions of the interfaces, but this code in common legacy code shared throughout our organization. As this is a potentially breaking change, I can't just go around making modifications all willy-nilly.)
Edit: Just to clarify, what I want is a simple way to be able to do the equivalent of this with Moq:
class C : B
{
public DateTime DateCreated { get; set; }
}
Since B and A both have a property with the same name and type, that single property serves both of them. It seems like since there's a simple way to do this in actual code there should be an equally simple way to do it with Moq.
After re-reading your question (I must not be awake) I realize I missed your point entirely. I don't see an easy way to do what you want, but if you want to live dangerously you could abuse the bug I mentioned below by setting up only the base interface's property:
[Test]
public void Constructor_Always_Succeeds()
{
var mockOfB = new Mock<B>();
var mockOfA = mockOfB.As<A>();
mockOfA.SetupProperty(p => p.DateCreated);
B b = mockOfB.Object;
A a = b;
DateTime aTime = DateTime.Now;
DateTime bTime = DateTime.Now.AddDays(-1);
a.DateCreated = aTime;
Assert.That(a.DateCreated, Is.EqualTo(aTime));
Assert.That(b.DateCreated, Is.EqualTo(aTime));
b.DateCreated = bTime;
Assert.That(a.DateCreated, Is.EqualTo(bTime));
Assert.That(b.DateCreated, Is.EqualTo(bTime));
}
This answers the question I thought was asked, not the one that was actually asked
I was able to get this to work using As<>()
, but with a twist. Here's my test:
[Test]
public void Constructor_Always_Succeeds()
{
var mockOfB = new Mock<B>();
var mockOfA = mockOfB.As<A>();
DateTime dateTime = DateTime.Now;
mockOfA.SetupGet(p => p.DateCreated).Returns(dateTime);
mockOfB.SetupGet(p => p.DateCreated).Returns(dateTime);
B b = mockOfB.Object;
A a = b;
Assert.That(b.DateCreated, Is.EqualTo(dateTime));
Assert.That(a.DateCreated, Is.EqualTo(dateTime));
}
That test passes, and without knowing more about your actual situation should work for you.
However, while initially testing this I had the SetupGet
calls reversed (and was thinking you wanted different return values), like this:
[Test]
public void Constructor_Always_Succeeds()
{
var mockOfB = new Mock<B>();
var mockOfA = mockOfB.As<A>();
DateTime aTime = DateTime.Now;
DateTime bTime = DateTime.Now.AddDays(-1);
// only difference, these two lines are swapped
mockOfB.SetupGet(p => p.DateCreated).Returns(bTime);
mockOfA.SetupGet(p => p.DateCreated).Returns(aTime);
B b = mockOfB.Object;
A a = b;
Assert.That(b.DateCreated, Is.EqualTo(bTime));
Assert.That(a.DateCreated, Is.EqualTo(aTime));
}
And in that case it fails at the first assertion because both calls to DateCreated
return aTime
. This is because of how Moq works internally, but I'd hesitate to call it a bug and there is a bug filed for it. When the call to the property is being intercepted, the setup that matches it is found with the following code:
localctx.Call = FluentMockContext.IsActive ?
(IProxyCall)null :
ctx.OrderedCalls.LastOrDefault(c => c.Matches(invocation));
In other words, "find the last setup that could be used as a match for this invocation." Since we setup A.DateCreated
last, it could be used as a call for either B
or A
's DateCreated
property.
Perhaps inside of MethodCall.Matches
this situation should be handled.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With