【问题标题】:c# how to test reference to interfacec#如何测试对接口的引用
【发布时间】:2012-11-28 18:32:43
【问题描述】:

我之前问过一个关于如何set value of interface in testing a method 的问题 .我已经成功地将 Moq 框架实施到我的项目中,并且测试运行良好。

这是我提供的示例代码:

public void PostEvent(
            eVtCompId inSenderComponentId, 
            eVtEvtId inEventId, 
            long inEventReference, 
            IF_SerializableData inEventData)
{
    if(mEventMap.ContainsKey(inEventId))
    {
        mEventMap[inEventId](inSenderComponentId, inEventReference, inEventData);
    }
}

这里我有 4 个参数:第一个:一个枚举,第二个:另一个枚举,第三个:long,第四个:一个接口。 然而,我错了,第四个参数(接口)不应该是接口,而是接口的reference。 p>

所以它应该是这样的:

public void PostEvent(
       eVtCompId inSenderComponentId, 
       eVtEvtId inEventId, 
       long inEventReference, 
       ref IF_SerializableData inEventData)

给我的示例 Moq 测试代码(就是这个)...

var serializable = new Mock<IF_SerializableData>();
target.PostEvent(..., serializable.Object);

...不起作用。我已经尝试过ref serializable.Object,但它仍然无法正常工作,因为我收到一个错误,提示 ref 参数期望引用一个变量,而不是一个对象。

关于如何正确测试的任何提示或示例?

【问题讨论】:

  • 考虑使用正确的命名约定。您的枚举名称几乎无用。 in 前缀是不需要的,因为 out 变量有一个特殊的指示符。
  • 为什么它们几乎没用?我不明白,你为什么要提出一些我认为在我的情况下甚至对我没有帮助的东西?命名约定是我最不关心的;我这样命名它们是因为这是一个要求。
  • 一个名字应该传达一个意思。你的名字不是这样的。我的评论旨在改善您的整体编码。我知道这并不能回答你的问题。这实际上是我将其发布为评论而不是答案的原因。
  • 哦,好吧。是的,我知道关于正确命名约定的交易,我从大学时代就一直在练习。但是,假设我以这种方式命名我的变量类型和变量名称因为我的老板这么说。他想要的那些命名约定对我目前分配的项目有意义并且实际上是有意义的。精明? :)
  • 好吧 :-)(我不会接受这样的命名约定,不管它们背后的原因是什么)

标签: c# unit-testing interface moq


【解决方案1】:

您需要将 Object 引用从 serializable 模拟复制到一个局部变量中,然后您可以将其作为 ref 传递。

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

您不能通过 ref Serializable.Object,因为它是一个属性 - 另请参阅 Is it possible to pass properties as "out" or "ref" parameters?,它提供了关于为什么会这样的精彩讨论,以及其他链接的来源。

我的解释

这归根结底是因为属性不是变量。读/写属性是一对 getset 访问器方法,提供类似变量的功能,但至关重要的是,当您 get 属性时,您总是会获得基础变量 - 即使该变量具有引用类型。

所以:

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; }
  }
}

在这个例子中 - 如果你 可以ref MyClass.Property 传递给一个方法 - 这将毫无意义,因为它将传递对堆栈上瞬态变量的引用 - 即引用的副本Propertyget 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` 返回的方法可以修改 - 当它根本不能时。

因此,在您的情况下,为什么我们需要从堆栈中捕获该值并将其复制到局部变量中。现在 - 请注意,为了让 ref 方法设置的新值出现在您的 Mock&lt;T&gt; 上,您需要再次将其 Object 属性设置为局部变量值(如果可以的话 - 我不会使用 Moq 但我认为它的 Mocks 是不可变的)。

关于 C# 是否应该以这种方式自动处理 ref Property 已经有很多争论(请参阅前面提到的我链接到的 SO)。在我看来,这类似于 ref Derivedref Base 不兼容 - 是的,有一种方法可以让语言自动为您处理这个问题,但应该这样做吗?在我看来,没有。我会因此感到沮丧吗?哦,是的,当然 - 但我经常发现它突出了确实应该修复的架构弱点(例如,依赖 refout 参数,返回值可能更好)。

C# 允许您通过引用传递属性的唯一方法是将 get 和 set 访问器传递给目标方法 - 这根本与 ref 不兼容(因为这只是一个内存位置)。

作为品尝者,您必须编写这样的方法:

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

有了这个,我们现在可以MyClass上提供类似ref的语义:

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

但这实在是太丑了。

创建一个获取ref MyClass 的方法更简单 - 然后它可以直接写入任何属性。

【讨论】:

  • 天哪,它有效 XDDD 你能向我解释为什么这是必要的吗?该代码背后实际发生了什么?谢谢一百万:)
  • 我已经做了解释——不确定它是否比其他人的更好,但我认为它触及了问题的核心
猜你喜欢
  • 2011-03-08
  • 1970-01-01
  • 1970-01-01
  • 2023-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-11-22
  • 1970-01-01
相关资源
最近更新 更多