如何使用Moq模拟Microsoft.Office.Interop.Excel.Range?

问题描述:

我想嘲笑Microsoft.Office.Interop.Excel.Range(和其他Microsoft.Office.Interop.Excel接口)来单元测试我的应用程序。我在C#中使用了Moq 4.0.10827和.NET 4。如何使用Moq模拟Microsoft.Office.Interop.Excel.Range?

在我的单元测试,我试图建立模拟的行为如下:

 var range = new Mock<Microsoft.Office.Interop.Excel.Range>(); 
     range.Setup(r => r.get_Value(1)).Returns("Something"); 

     var classBeingTested = new MyClass(range.Object); 

在代码是单元测试,我使用的范围如下:

public MyClass(Range range) 
    { 
     var value = range[1].ToString(); 
    } 

当测试运行时,它产生了以下异常:

 Error: Missing method 'instance object [My.Example.Implementation.MyClass] Microsoft.Office.Interop.Excel.Range::get__Default(object,object)' from class 'Castle.Proxies.RangeProxy'. 

我怎样才能实现这个mockin g成功了吗?我意识到使用诸如https://*.com/a/9486226/1548950等模式隔离Excel Interop功能是一个潜在的解决方案;不过,如果能够从Microsoft.Office.Interop.Excel接口创建模拟,我会更开心。

编辑:在这个问题上

确定更多细节,这很奇怪。我已经创建了一个项目来测试jimmy_keen的建议。有用。然而,当我用同样的建议,测试的项目我有问题有, 它抛出以下异常:

Error: Missing method 'instance object [My.Example.Implementation.MyClass] Microsoft.Office.Interop.Excel.Range::get__Default(object,object)' from class 'Castle.Proxies.RangeProxy'. 

由于jimmy_keen的建议工作在其他项目很好,我开始怀疑,有一些错误的项目我正在测试代码。所以,我创建了一个测试 溶液演示详细该问题:http://www7.zippyshare.com/v/70834394/file.html

的问题的根源似乎是该项目包含另一个类(即不被单元测试)具有基本如下代码:

using Microsoft.Office.Interop.Excel; 
namespace Project 
{ 
    public class UnTestedClass 
    { 
     public UnTestedClass(Worksheet sheet) 
     { 
      var foo = sheet.Range["Description"].Column; 
     } 
    } 
} 

我不明白为什么会导致这个问题,因为我没有在单元测试中使用UnTestedClass。我在如何使用Moq时错过了一些东西,或者我偶然发现了一个错误?

+0

+1这很奇怪,我猜它与COM对象有关。只要我参考了excel interop,我就不能再引用我的Moq库了......怪异的... – jsmith 2012-07-24 14:51:43

+0

在完成了一些工作之后,我现在正朝着隔离Microsoft.Office.Interop.Excel功能的方向努力自定义类和接口,而不是直接模拟Microsoft.Office.Interop.Excel。*。虽然这个问题的答案确实解决了当前的问题,但嘲笑Microsoft.Office.Interop.Excel。*最终只是开始需要太多的设置工作才能使其有用。 – 2012-08-03 05:52:29

你的代码访问索引,这就是你需要模拟什么:

var range = new Mock<Microsoft.Office.Interop.Excel.Range>(); 
range.Setup(r => r[1, It.IsAny<object>()]).Returns("something"); 

注意Range索引其实需要两个参数 - 因此在呼叫It.IsAny<object>()约束。

+0

谢谢,我测试了它,它确实在某些情况下工作。但是,我正在进行的单元测试仍然顽固地拒绝工作。请参阅我上传的测试解决方案http://www7.zippyshare.com/v/70834394/file.html – 2012-07-25 07:13:13

+2

@LauriHarpf:这很有趣。显然这是编译器生成代码的一个“问题”。编译器在* Project *中生成的'Range'类型接口具有额外的属性(即'Column',即)。我不知道这对于Castle来说可能会有问题......但它是COM。如果你用ILSpy检查你的程序集,你会明白我的意思。编译器生成的代码不同(甚至类都是相同的),这一定是问题所在。请注意,如果存根'range.Setup(r => r.Column).Returns(0);'这个测试也会通过。 – 2012-07-25 21:58:06

+1

非常好,谢谢! ILSpy的提示也很有用;在我的真实项目中,除了列外,还会看到其他额外的属性。目前是“列”,“列”,“行”,“行”和“值”。这种行为可能会使我写的任何单元测试都变得非常脆弱,因为在任何时候新的属性都可能出现并破坏某些东西。无论如何,通过使用你的提示并仔细地模拟所有这些工作的额外属性,所以标记为答案。谢谢你,先生! – 2012-07-27 11:52:58