You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在单元测试项目中实例化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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 14:55:28