11

我有一个包含大约 500 个单元测试的 .NET 库项目。所有这些测试在 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'.

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

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

现在我可以想象最小起订量可能不适用于互操作类型。但我觉得最令人费解的是,这些测试在 Visual Studio 2012 中运行良好,但在 Visual Studio 2010 中却失败了。

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

更新:2012 年 3 月 11 日

好的,所以我把它归结为:

  • 我有两个项目;核心和 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 属性。
  • 虽然这两种类型不同并且不共享一个公共接口或超类,但它们是等价的。这意味着 CLR 会将它们视为相同,并允许您跨程序集边界使用这些类型。例如,Core.UnitTest 中的 ListRow 实例在传递给 Core 库中的方法时可以正常工作。共享的 Range 属性将起作用,而缺少的 Index 属性将在访问时引发 MissingMethodException。
  • 上述行为甚至适用于模拟类型。Mock[Excel.ListRow] 的模拟对象在跨越程序集边界时可以正常工作。
  • 不幸的是,前一点中描述的行为仅在我在 Visual Studio 2012中构建程序集时才有效。当我在 Visual Studio 2010中构建我的程序集并调试我的代码时,我可以看到模拟的 ListRow 实例被传递到我的核心项目的方法中。当实例越过程序集边界时,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 项目组成。两者都引用了启用 EIT 的 Microsoft.Office.Interop.Excel.Range。该测试在 VS2012 中运行良好,但在 VS2010 中抛出 MissingMethodException。在测试中取消注释虚拟行将使其在 VS2010 中工作。

最后更新:29-12-2012

我为迟到的更新道歉。我的一位同事找到了一个解决方案,但是我无法在我的机器上重现它。与此同时,我们公司已经切换到 TFS2012,所以这对我来说不再是一个阻塞问题。我的同事得出的两个最重要的结论是:

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

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

4

4 回答 4

6

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

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


我设法让它在 VS2010 中工作:

在此处输入图像描述

这是我的解决方案http://temp-share.com/show/Pf3Ypip62为大家提供方便。它包含所有起订量参考。我有 Excel 2007(即 v12) - 所以请调整对 Office 14 的引用。

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

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

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

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

我遇到了很多麻烦,请参阅此处如何在模拟 Excel.worksheet 时避免使用动态? 并且还看到我问的这个问题:Mocked object doesn't have all properties shown in Intellisense - 在一个项目中,但在另一个项目中

无论如何,这是让它在 VS 2010 中工作的方法。我很高兴它在 2012 年得到解决!

于 2012-11-11T03:44:57.700 回答
3

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

当您使用“Embed Interop Types”编译项目时,C# 编译器会生成一个内部类型,该类型只有您正在访问的成员,而实现实际上似乎使用 IDispatch 通过 id 调用 COM 对象的方法。

根据您的描述,我了解到您在 VS 2012 中的测试项目无法访问属性(甚至无法模拟它们),但测试仍然成功,并且测试项目中生成的类型确实具有这些成员。

如果这确实是您遇到的情况,您能否查看您的测试 .dll 的内容并查看互操作类型是如何生成的?您可以使用诸如ildasm.exe.

如果测试 .dll 中的互操作类型包含所有成员,即使是您在测试中未访问的成员,这可能会提示差异与 VS 2012 中生成互操作类型的方式有关。

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

于 2012-11-08T21:57:30.250 回答
0

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

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

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

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

希望它会工作..

于 2012-11-08T06:46:53.187 回答
0

我发现,至少在 VS2015 中,我仍然可以将互操作类型嵌入到我的测试程序集中,但是在我的测试项目中的 PIA 程序集引用上将“Embed”设置为 false,我没有对此进行复制。

于 2017-01-13T00:00:53.683 回答