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

WPF OnSelectionChanged方法单元测试参数初始化报错

WPF OnSelectionChanged方法单元测试参数构造问题

问题背景

待测试的OnSelectionChanged为静态业务方法,接收object类型sender(生产环境传入TreeView控件实例)、ItemSelectionChangedEventArgs类型参数(用于获取当前选中的文件夹项),返回ObservableCollection<Folder>类型的目录/文件集合,方法实现如下:

public static ObservableCollection<Folder> OnSelectionChanged(object sender, ItemSelectionChangedEventArgs itemSelection)
{
    if (sender == null)
    {
        throw new ArgumentNullException("Can't be null", nameof(sender));
    }
    if (itemSelection == null)
    {
        throw new ArgumentNullException("Can't be null", nameof(itemSelection));
    }

    var selected = itemSelection.AddedItems[0] as Folder;

    DirectoryInfo dirInfo = new DirectoryInfo(selected.FullName.ToString());
    ObservableCollection<Folder> directories = new ObservableCollection<Folder>();

    directories = AddDirectoryInfo(directories, dirInfo);
    directories = AddFileInfo(directories, dirInfo);
    
    return directories;
}

编写测试时初始化参数出现两类异常:

  • 实例化SfTreeView作为sender时抛出:System.InvalidOperationException: 'The calling thread should be STA, since most UI components require it.'
  • 实例化ItemSelectionChangedEventArgs作为事件参数时抛出:System.NullReferenceException: 'Object reference not set to an instance of an object.'
    原测试代码如下:
private readonly string _folderName = "C:\\NewTestFolder";
[TestMethod()]
public void OnSelectionChangedTest()
{
    var sender = new SfTreeView() { Name = "treeSize" };

    var itemSelection = new ItemSelectionChangedEventArgs()
    {
        AddedItems = { new Folder()
        {
            FullName = _folderName,
            HasChildrenNodes = false,
            Modified = DateTime.Now.ToString(),
            Name = "NewTestFolder",
            Size = "0",
            Type = "Folder"
        } },
        RemovedItems = { new Folder() { FullName = "" } }
    };

    var result = FolderView.OnSelectionChanged(sender, itemSelection);
}

错误原因

  1. STA线程报错原因:WPF及第三方UI控件(如Syncfusion的SfTreeView)必须在STA线程下创建,而单元测试默认运行在MTA线程中,直接实例化UI控件就会触发线程模式异常。实际上查看业务方法逻辑可以发现,方法内仅对sender做了非空校验,没有访问sender的任何属性、方法,完全不需要传入真实的SfTreeView实例。
  2. 空引用报错原因:ItemSelectionChangedEventArgs的AddedItems、RemovedItems属性默认值为null,使用集合初始化器语法直接向这两个属性添加项时,本质是对null值调用Add()方法,必然触发空引用异常。同时这类事件参数通常没有暴露公开的无参构造逻辑,直接new实例本身就不符合类型设计预期。

修复方案

1. 绕开UI控件实例化解决STA问题

单元测试只需要满足方法的前置校验要求即可,不需要完全模拟生产环境的入参,sender参数传入任意非null对象就能通过校验,比如直接传new object(),完全不需要实例化重型UI控件,从根源避免STA线程问题。

2. 正确构造事件参数

推荐使用Mock框架伪造事件参数实例,不需要调用类型的真实构造函数,也不需要手动初始化内部集合,以Moq框架为例,测试代码修改如下:

private readonly string _folderName = "C:\\NewTestFolder";
[TestMethod()]
public void OnSelectionChangedTest()
{
    // 传入任意非null对象作为sender,通过非空校验即可
    var sender = new object();

    // Mock构造事件参数
    var mockArgs = new Mock<ItemSelectionChangedEventArgs>();
    var testFolder = new Folder()
    {
        FullName = _folderName,
        HasChildrenNodes = false,
        Modified = DateTime.Now.ToString(),
        Name = "NewTestFolder",
        Size = "0",
        Type = "Folder"
    };
    // 配置AddedItems返回包含测试文件夹的集合
    mockArgs.SetupGet(a => a.AddedItems).Returns(new List<object> { testFolder });
    var itemSelection = mockArgs.Object;

    // 注意:测试前需确保C:\NewTestFolder路径真实存在,否则DirectoryInfo访问会抛异常
    var result = FolderView.OnSelectionChanged(sender, itemSelection);
    
    // 补充断言逻辑校验返回结果
    Assert.IsNotNull(result);
}

如果你使用的ItemSelectionChangedEventArgs版本允许直接给AddedItems、RemovedItems属性赋值,也可以手动先初始化集合再添加项,不需要Mock:

var itemSelection = new ItemSelectionChangedEventArgs()
{
    // 先初始化集合实例,再添加元素
    AddedItems = new List<object>
    {
        new Folder()
        {
            FullName = _folderName,
            HasChildrenNodes = false,
            Modified = DateTime.Now.ToString(),
            Name = "NewTestFolder",
            Size = "0",
            Type = "Folder"
        }
    },
    RemovedItems = new List<object>
    {
        new Folder() { FullName = "" }
    }
};

补充提示:测试执行前需要确认测试用的目录路径真实存在,否则方法内DirectoryInfo读取目录信息时会抛出目录不存在异常,导致测试失败。


内容的提问来源于stack exchange,提问作者Tort Sweet

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 08:27:20