如何初始化ThisAddIn类开展VSTO插件单元测试?
直接实例化ThisAddIn编写单元测试是否可行
这是完全错误的实现思路。
ThisAddIn是和VSTO运行时、Office COM上下文深度绑定的类,它的完整初始化流程、生命周期管理、依赖的各类底层服务全都是由VSTO运行时在Office宿主进程内部托管完成的。哪怕你凑齐了构造函数的两个参数手动new出实例,这个实例也不具备真实加载环境下的运行状态:Globals对象状态异常、Office互操作服务调用失败、生命周期事件不会按预期触发,基于这种实例写的测试完全不能反映插件的真实运行情况,没有任何测试价值,还会浪费大量时间在补依赖上。
正确的单元测试写法非常简单:把插件里的业务逻辑从ThisAddIn这个薄外壳里剥离出来,封装成独立的、不依赖VSTO和Office COM组件的普通类库。这部分纯业务逻辑没有任何特殊运行时依赖,用xUnit、MSTest这类常规测试框架就能直接写单元测试,不需要碰ThisAddIn的实例化问题。
如果你的测试目标是验证插件加载、和Outlook交互这类和宿主强相关的逻辑,这属于集成测试范畴,需要在真实Outlook进程加载插件的环境下执行,不属于单元测试的覆盖范围。
构造ThisAddIn所需入参的方式(仅作技术探讨,强烈不推荐用于测试场景)
ThisAddIn构造函数需要两个入参,两个参数都没有公开的可直接实例化的官方实现,只能通过Mock框架模拟:
Microsoft.Office.Tools.Outlook.Factory:这是VSTO运行时内部实现的接口,没有对外暴露可直接new的实现类。如果要模拟,需要用Mock框架生成接口代理,同时把接口定义的数十个成员全部按照VSTO运行时的内部行为配置返回规则,工作量极大。System.IServiceProvider:这是VSTO运行时传递服务入口的参数,基类构造阶段就会通过它查询Outlook应用实例、VSTO核心服务等十余个依赖,只要有一个查询返回不符合预期,基类初始化直接抛出异常。
用Moq框架模拟参数的示例代码如下(仅作演示,实际运行会因大量依赖未配置直接报错):
using Moq; using Microsoft.Office.Tools.Outlook; using Microsoft.Office.Interop.Outlook; using System; using NS; // 以下代码仅作参数构造演示,无法支撑插件正常运行 var mockFactory = new Mock<Factory>(); var mockServiceProvider = new Mock<IServiceProvider>(); var mockApp = new Mock<Application>(); // 配置最基础的服务返回,否则基类构造直接失败 mockServiceProvider.Setup(sp => sp.GetService(It.IsAny<Type>())) .Returns(mockApp.Object); Globals.Factory = mockFactory.Object; // 即使成功创建实例,后续所有依赖VSTO运行时的逻辑都会异常 var testAddIn = new ThisAddIn(mockFactory.Object, mockServiceProvider.Object);
注意:不要在实际测试项目中使用这种Mock实例的方案,你花费大量时间补全所有Mock规则后,测的也只是你自己模拟出来的假运行环境,和真实插件在Outlook中的运行逻辑差异极大,测试结果完全不可信。
内容的提问来源于stack exchange,提问作者Akanksha_p

