我有一些界面,我试图使用以下结构进行模拟(简化):
interface A
{
DateTime DateCreated { get; set; }
}
interface B : A
{
DateTime DateCreated { get; set; }
}
我遇到的问题是DateCreated是每个接口的独立属性,所以即使我所知道的具体对象只有这些共享属性的一个共享实现,也要调用Mock.SetupAllProperties给每个人一个单独的实现。这意味着它们不共享值,因此调用((B)obj).DateCreated = {blah}在((A)obj).DateCreated被访问时不会在其他地方给出所需的结果。
我认为在Moq中解决这个问题的唯一方法是执行以下操作:
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; });
这很乏味,容易出错,我需要为至少十几个属性执行此操作,而且我不知道还有多少属性。我可能会写一个通用的方法来做到这一点,但似乎必须有一个更简单的解决方案。任何人都可以提出更好的方法吗?
(我喜欢&#34;正确&#34;接口的定义,但这些代码在我们组织中共享的常见遗留代码中。由于这是一个潜在的重大变化,我可以&#39;我只是无所事事地进行修改。)
编辑:只是为了澄清,我想要的是一种简单的方法,可以用Moq做相同的事情:
class C : B
{
public DateTime DateCreated { get; set; }
}
由于B和A都具有相同名称和类型的属性,因此该单个属性同时为它们提供服务。看起来因为在实际代码中有一个简单的方法可以用Moq做同样简单的方法。
答案 0 :(得分:4)
重新阅读你的问题后(我一定不醒)我意识到我完全错过了你的观点。我没有看到一个简单的方法来做你想要的,但如果你想生活危险,你可以通过只设置基本接口的属性滥用我下面提到的错误:
[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));
}
这回答了我认为被问到的问题,而不是实际问过的问题
我能够使用As<>()
来实现这一点,但有一点麻烦。这是我的测试:
[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));
}
该测试通过,并且在不了解您的实际情况的情况下应该适合您。
然而,在最初测试时,我调用了SetupGet
个调用(并且认为你想要不同的返回值),如下所示:
[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));
}
在这种情况下,它在第一次断言时失败,因为对DateCreated
的两次调用都返回aTime
。这是因为Moq如何在内部工作,但是我会毫不犹豫地将其称为bug 和there is a bug filed for it。当拦截对该属性的调用时,可以使用the following code找到与其匹配的设置:
localctx.Call = FluentMockContext.IsActive ?
(IProxyCall)null :
ctx.OrderedCalls.LastOrDefault(c => c.Matches(invocation));
换句话说,“找到可用作此调用匹配项的最后一个设置。”由于我们最后设置了A.DateCreated
,因此可以将其用作对B
或A
的{{1}}属性的调用。
也许在MethodCall.Matches
内部应该处理这种情况。