Moq&互操作类型:在VS2012中工作,在VS2010中失败?

时间:2012-11-02 19:52:15

标签: c# visual-studio-2010 visual-studio-2012 moq excel-interop

我有一个.NET库项目,大约有500个单元测试。所有这些测试在Visual Studio 2012中运行良好。但是,我的一些测试在Visual Studio 2010中失败。在这些失败的测试中,我使用 Moq 来模拟来自Microsoft.Office.Interop.Excel的几个互操作类型。尝试访问这些模拟的互操作类型时,测试立即失败:

Error: Missing method 'instance class Microsoft.Office.Interop.Excel.Range [ExcelAddIn.Core] Microsoft.Office.Interop.Excel.ListRow::get_Range()' from class 'Castle.Proxies.ListRowProxy'.

此异常意味着我忘记在模拟上设置适当的属性getter。事实并非如此:

_listRowMock.Setup(m => m.Range).Returns(_rangeMock.Object);

现在我可以想象Moq对Interop Types的效果可能不太好。但我发现最令人费解的是,这些测试在Visual Studio 2012中运行良好,但在Visual Studio 2010中失败。

为什么我的Visual Studio会影响我的代码行为?

更新时间:2012年3月3日

好的,所以我明白了这一点:

  • 我有两个项目; Core和Core.UnitTest。 Core是实际的库,而Core.UnitTest是Core库的单元测试项目。
  • 两个项目都引用了Microsoft.Office.Interop.Excel并启用了嵌入互操作类型。
  • 由于启用了EIT,因此两个项目都包含自己的Microsoft.Office.Interop.Excel库“视图”。该视图包含其各自项目中使用的所有类,方法和属性。
  • 由于两个项目都使用Microsoft.Office.Interop.Excel的不同类,方法和属性,因此两个库的嵌入类型不同。例如。 Core中的ListRow有一个Index和Range属性,而Core.UnitTest中的ListRow只有Range属性。
  • 虽然两种类型都不同,并且不共享通用界面或超类,但它们是 equivalent 。这意味着CLR会将它们视为相同,并允许您跨程序集边界使用这些类型。例如。 Core.UnitTest中的ListRow实例在传递给Core库中的方法时可以正常工作。共享的Range属性将起作用,而缺少的Index属性将在访问时抛出MissingMethodException。
  • 上述行为甚至适用于模拟类型。 Mock [Excel.ListRow]的模拟对象在穿过装配边界时可以正常工作。
  • 不幸的是,前一点中描述的行为仅在我在Visual Studio 2012 中构建程序集时才有效。当我在Visual Studio 2010 中构建程序集并调试我的代码时,我可以看到模拟的ListRow实例被传递到我的Core项目的方法中。当实例越过程序集边界时,ListRow的所有方法和属性都会丢失它们的实现并抛出MissingMethodExceptions。
  • 现在,对于有趣的部分,我实际上通过确保两种嵌入式ListRow对齐来设法缓解此问题。例如。为了让编译器在两个项目中创建相同的ListRow视图,我确保在UnitTest项目中使用了完全相同的方法和属性。这意味着添加虚拟行,如:var dummy = listRow.Index。一旦我让编译器创建了我的嵌入式ListRow类型的相同视图,就允许该实例跨越程序集边界而不会丢失其实现。

问题仍然存在:导致Visual Studio 2010和Visual Studio 2012之间的行为差​​异的原因是什么?

更新时间:2012年9月11日

演示解决方案http://temp-share.com/show/KdPf6066h

我已经创建了一个小解决方案来演示效果。该解决方案由一个库和一个UnitTest项目组成。两者都引用了Microsoft.Office.Interop.Excel.Range并启用了EIT。该测试在VS2012中正常工作,但在VS2010中抛出MissingMethodException。在测试中取消注释虚拟线将使其在VS2010中工作。

最终更新:2012年12月29日

我对最新的更新表示歉意。我的一位同事找到了解决方案,但是我无法在我的机器上重现它。与此同时,我们公司已经转向TFS2012,因此这不再是我的阻塞问题。我的同事做出的两个最重要的结论是:

  • “Any CPU”平台的语义已从Visual更改 Studio 2010到Visual Studio 2012.这将导致不同 .DLL的生成取决于您使用的是VS2010还是 VS2012。
  • 这两个项目都引用了不同版本的Microsoft.Office.Interop.Excel。

我检查了我的项目并理顺了参考文献,但没有任何区别。之后,我在VS2010和VS2012中尝试了不同的平台变体,但无法产生令人满意的结果。我会接受杰里米的回答,因为这是最有帮助的。谢谢大家的帮助。

4 个答案:

答案 0 :(得分:5)

编辑:当我在Visual Studio 2012中尝试并且目标.Net 4.0时,它适用于我,只使用.Net PIA而不是COM参考。相同的解决方案在VS2010中不起作用。

VS2010加载Microsoft.VisualStudio.QualityTools.UnitTestFramework.dll's的版本10.0.30319.1和VS2012加载版本11.0.50727.1。您可以在“模块”窗口中看到不同的版本。


我设法让它在VS2010中运行:

enter image description here

以下是我的解决方案http://temp-share.com/show/Pf3Ypip62,方便大家。它包含所有Moq参考。我有Excel 2007(即第12版) - 所以请调整对Office 14的引用。

包含待测方法的项目必须通过.Net参考选项卡使用PIA Microsoft.Office.Interop.Excel

在单元测试项目中,您必须通过COM参考选项卡使用Microsoft Excel 1X.0 Object Library - 它是一个ActiveX。

  

令人困惑的是,在解决方案资源管理器中,它们都被调用:Microsoft.Office.Interop.Excel

还有一个警告,我不知道如何解决方法 - 你必须使用.Net 3.5框架我实际上希望微软在2012年修复它,因为我无法工作如何与.Net 4.0中的所有项目一起完成。针对.Net 3.5&amp ;;的混合项目的一些解决方案4.0还可以。

我遇到了很多麻烦,请点击此处How do I avoid using dynamic when mocking an Excel.worksheet?并看到我问过的这个问题:Mocked object doesn't have all properties shown in Intellisense - in one project but has them in the other

无论如何,这是如何让它在VS 2010中运行。我很高兴它在2012年得到解决!

答案 1 :(得分:3)

我试图重现这一点,对我来说,即使在VS 2012中它也不起作用。

使用“嵌入互操作类型”编译项目时,C#编译器会生成一个只有您正在访问的成员的内部类型,并且实现似乎实际上似乎使用IDispatch通过id调用COM对象的方法。

从您的描述中我了解到,您在VS 2012中的测试项目不会访问属性(甚至不是为了模拟它们),但测试仍然成功并且测试项目中生成的类型 拥有这些成员。

如果您确实遇到了这种情况,请查看测试内容.dll并查看互操作类型是如何生成的?您可以使用ildasm.exe等工具。

如果测试.dll中的互操作类型包含所有成员,即使是那些在测试中不访问的成员,这可能会给出一个线索,即差异与VS 2012中生成互操作类型的方式有关。

另外,如果您可以附加一个小的最小VS 2012解决方案来重现问题,那么它可能对诊断它有很大帮助。

答案 2 :(得分:0)

首先检查在VS2010中向项目添加库时,确保创建了模拟对象,如

Mock<DocumentService> _mock = new Mock<DocumentService>();

.NET 4.0允许将主互操作程序集嵌入到程序集中,这样就不需要将它们与应用程序一起部署。在VS2010中打开程序集中的属性选项卡并检查嵌入式互操作类型.mkae确定它的真。

要实例化excel,Excel.Application xlapp = new Excel.Application();

希望它能起作用..

答案 3 :(得分:0)

我发现,至少在VS2015中,我仍然可以在我的测试组件中嵌入互操作类型,但在我的测试项目中将PIA程序集引用上的“Embed”设置为false,我没有重复这个。

相关问题