如何在单元测试项目中实例化VSTO加载项?解决空引用异常
Visio VSTO加载项单元测试空引用异常解决方法
问题原因
你在单元测试里直接调用Globals.Factory.GetRibbonFactory()触发空引用,核心原因是VSTO的Globals对象依赖Office宿主环境(这里是Visio进程)完成初始化,单元测试运行时没有启动Visio进程,导致Globals.Factory为null,进而抛出空引用异常。
解决方案:通过依赖注入+模拟框架隔离宿主依赖
VSTO代码默认强耦合Office宿主,要做单元测试必须先解耦,推荐用依赖注入+Moq这类模拟框架实现隔离:
1. 抽象RibbonFactory依赖
先把VSTO原生的IRibbonFactory包装成自定义抽象接口,避免直接依赖Globals:
// 定义抽象接口,只暴露业务需要的方法 public interface IRibbonFactoryWrapper { IRibbonControl CreateRibbonControl(); // 根据你的业务需求添加其他需要的方法 } // 实现真实环境下的包装类,对接VSTO原生工厂 public class RibbonFactoryWrapper : IRibbonFactoryWrapper { private readonly IRibbonFactory _innerFactory; public RibbonFactoryWrapper(IRibbonFactory innerFactory) { _innerFactory = innerFactory; } public IRibbonControl CreateRibbonControl() { return _innerFactory.CreateRibbonControl(); } }
2. 重构你的VSTO业务类
让你的Ribbon类或核心业务类依赖抽象接口,而非直接调用Globals.Factory:
public class YourVisioRibbon : Office.IRibbonExtensibility { private readonly IRibbonFactoryWrapper _ribbonFactory; // 通过构造函数注入依赖,解耦宿主环境 public YourVisioRibbon(IRibbonFactoryWrapper ribbonFactory) { _ribbonFactory = ribbonFactory; } // 业务方法示例 public IRibbonControl CreateCustomControl() { return _ribbonFactory.CreateRibbonControl(); } // 实现VSTO必需的接口方法(如GetCustomUI等) public string GetCustomUI(string ribbonID) { // 你的Ribbon XML逻辑 return "<customUI xmlns=\"http://schemas.microsoft.com/office/2009/07/customui\"></customUI>"; } }
3. 单元测试中模拟依赖
用Moq框架模拟IRibbonFactoryWrapper,无需启动Visio即可完成单元测试:
[TestClass] public class UnitTest1 { private Mock<IRibbonFactoryWrapper> _mockFactory; private YourVisioRibbon _testRibbon; [TestInitialize] public void Setup() { // 创建模拟对象 _mockFactory = new Mock<IRibbonFactoryWrapper>(); // 设置模拟方法的返回值 _mockFactory.Setup(f => f.CreateRibbonControl()) .Returns(Mock.Of<IRibbonControl>()); // 初始化测试对象,注入模拟依赖 _testRibbon = new YourVisioRibbon(_mockFactory.Object); } [TestMethod] public void CreateCustomControl_ShouldCallFactoryMethod() { // 执行测试方法 var result = _testRibbon.CreateCustomControl(); // 验证模拟方法是否被调用 _mockFactory.Verify(f => f.CreateRibbonControl(), Times.Once); // 验证返回值有效性 Assert.IsNotNull(result); } }
备选方案:集成测试(不推荐用于单元测试)
如果必须测试和Visio宿主直接交互的逻辑,可以在测试中启动Visio实例,但这种方式属于集成测试,运行速度慢且依赖本地Office环境:
[TestMethod] public void IntegrationTestWithVisio() { // 启动Visio实例 var visioApp = new Microsoft.Office.Interop.Visio.Application(); try { // Visio进程启动后,Globals.Factory会被初始化 var ribbonFactory = Globals.Factory.GetRibbonFactory(); var ribbon = new YourVisioRibbon(new RibbonFactoryWrapper(ribbonFactory)); // 执行需要和宿主交互的测试逻辑 } finally { // 关闭Visio并释放COM对象 visioApp.Quit(); System.Runtime.InteropServices.Marshal.ReleaseComObject(visioApp); } }
内容的提问来源于stack exchange,提问作者Nihar Sahoo
相关产品推荐
相关产品推荐

