Moq:设置属性的简单方法,属性是多个接口的一部分

时间:2015-01-21 23:08:40

标签: c# moq

我有一些界面,我试图使用以下结构进行模拟(简化):

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做同样简单的方法。

1 个答案:

答案 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,因此可以将其用作对BA的{​​{1}}属性的调用。

也许在MethodCall.Matches内部应该处理这种情况。