Mocking和Marshal.ReleaseComObject()

时间:2014-02-12 12:07:19

标签: c# unit-testing mocking moq

我在设置模拟时遇到问题,因此我可以在我的Mocked对象上调用Marshal.ReleaseComObject()

我正在使用Moq设置类型为IFeature的模拟(来自第三方接口库)。模拟设置非常简单:

  var featureMock = new Mock<IFeature>(); 
  IFeature feature = featureMock.Object; 

在我的代码中,功能对象是在while循环中创建的,通过一种游标(FeatureCursor)运行。由于第三方库的遗留问题,Feature对象已知存在内存泄漏问题。因此,我必须通过Marshal.ReleaseComObject()释放对象,如代码中所示;

public class XXX
{

      public void DoThis()
      {
        IFeatureCursor featureCursor; 
        //...fill the cursor with features; 

        IFeature feature = null; 
        while ((feature = featureCursor.NextFeature)!= null)
        {
           //Do my stuff with the feature
          Marshal.ReleaseComObject(feature); 
        }

      }

}

当我使用真实的特征光标和特征时它可以工作,但是当我在单元测试中模拟该特征时,我收到一个错误:

"System.ArgumentException : The object's type must be __ComObject or derived from __ComObject."

但是如何将它应用于我的Mock对象?

1 个答案:

答案 0 :(得分:5)

Mocked IFeature只是一个标准的.NET类,而不是COM对象,这就是为什么你的测试当前抛出The object's type must be __ComObject...异常的原因。

您只需将调用包装到Marshal.ReleaseComObject(feature);并首先检查对象是否为COM对象:

if (Marshal.IsComObject(feature)
{
    Marshal.ReleaseComObject(feature);
}

然后你的测试将通过,但不会调用Marshal.ReleaseComObject(生产代码会调用它)。

因为听起来你实际上想要验证代码调用了Marshal.ReleaseComObject,你需要做更多的工作。

因为它是一个静态方法,并且实际上并没有对对象本身做任何事情,所以你唯一的选择就是创建一个包装器:

public interface IMarshal
{
    void ReleaseComObject(object obj);
}

public class MarshalWrapper : IMarshal
{
    public void ReleaseComObject(object obj)
    {
        if (Marshal.IsComObject(obj))
        {
            Marshal.ReleaseComObject(obj);
        }
    }
}

然后让您的代码依赖IMarshal,您也可以在测试中模拟并验证:

public void FeaturesAreReleasedCorrectly()
{
    var mockFeature = new Mock<IFeature>();
    var mockMarshal = new Mock<IMarshal>();

    // code which calls IFeature and IMarshal
    var thing = new Thing(mockFeature.Object, mockMarshal.Object);
    thing.DoThis();

    // Verify that the correct number of features were released
    mockMarshal.Verify(x => x.ReleaseComObject(It.IsAny<IFeature>()), Times.Exactly(5));
}