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

如何使用Xunit测试WPF的DataTableModel泛型类?

针对DataTableModel的Xunit单元测试解决方案

1. 测试保护方法(InstantiateTable、TriggerUpdate等)

直接调用保护方法有两种实用方式:

  • 派生测试类暴露方法:写一个继承自DataTableModel<T>的测试类,把需要测试的保护方法包装成公共方法:
public class TestableDataTableModel<T> : DataTableModel<T>
{
    // 按需暴露需要测试的保护方法
    public new void InstantiateTable() => base.InstantiateTable();
    public new void TriggerUpdate() => base.TriggerUpdate();
    public new void UpdateTable() => base.UpdateTable();
}

测试时直接实例化TestableDataTableModel<T>,调用这些公共包装方法即可。

  • 反射调用:如果不想修改类结构,用反射获取保护方法并执行:
var model = new DataTableModel<TestItem>();
var method = typeof(DataTableModel<TestItem>).GetMethod("TriggerUpdate", BindingFlags.Instance | BindingFlags.NonPublic);
method.Invoke(model, null);

2. 验证构造函数中的命令对象

假设构造函数初始化了ICommand类型的命令(比如WPF常用的RelayCommand):

  • 先通过测试类暴露命令(如果命令是protected),或者用反射获取私有命令字段;
  • 测试命令的CanExecute初始状态,以及执行Execute后的逻辑变化:
[Fact]
public void Constructor_InitializesCommands()
{
    var model = new TestableDataTableModel<TestItem>();
    // 假设Testable类暴露了构造函数中初始化的RefreshCommand
    var refreshCmd = model.RefreshCommand;

    Assert.NotNull(refreshCmd);
    Assert.True(refreshCmd.CanExecute(null)); // 验证初始可执行状态

    // 执行命令后验证数据更新逻辑
    refreshCmd.Execute(null);
    Assert.NotNull(model.AllItems);
}

3. 分页逻辑测试

构造模拟数据集,设置分页参数后验证结果:

[Fact]
public void Pagination_ReturnsCorrectPageData()
{
    // 构造25条测试数据
    var testItems = Enumerable.Range(1, 25).Select(i => new TestItem { Id = i }).ToList();
    var model = new TestableDataTableModel<TestItem>();
    // 假设Testable类提供设置内部数据源的方法
    model.SetInternalData(testItems);
    
    // 设置分页参数
    model.PageSize = 10;
    model.CurrentPage = 2;

    // 触发数据更新
    model.TriggerUpdate();

    // 断言分页结果
    Assert.Equal(10, model.CurrentPageItems.Count);
    Assert.Equal(11, model.CurrentPageItems.First().Id);
    Assert.Equal(3, model.TotalPages);
}

4. Filter委托测试

给Filter属性赋值自定义逻辑,验证过滤结果:

[Fact]
public void Filter_AppliesExpectedLogic()
{
    var testItems = new List<TestItem>
    {
        new TestItem { Id = 1, Category = "Fruit" },
        new TestItem { Id = 2, Category = "Vegetable" },
        new TestItem { Id = 3, Category = "Fruit" }
    };
    var model = new TestableDataTableModel<TestItem>();
    model.SetInternalData(testItems);

    // 设置过滤委托
    model.Filter = item => item.Category == "Fruit";
    model.TriggerUpdate();

    // 断言过滤后的结果
    Assert.Equal(2, model.FilteredItems.Count);
    Assert.All(model.FilteredItems, item => Assert.Equal("Fruit", item.Category));
}

5. Mock依赖的实现

如果DataTableModel<T>依赖外部服务(比如数据仓储),用Moq库模拟依赖:

[Fact]
public void UpdateTable_FetchesDataFromRepository()
{
    // 模拟仓储接口
    var mockRepo = new Mock<IItemRepository>();
    var expectedData = new List<TestItem> { new TestItem { Id = 1 } };
    mockRepo.Setup(repo => repo.GetAll()).Returns(expectedData);

    // 注入模拟依赖实例化模型
    var testableModel = new TestableDataTableModel<TestItem>(mockRepo.Object);

    // 触发数据更新
    testableModel.TriggerUpdate();

    // 验证仓储方法是否被调用,以及数据是否正确加载
    mockRepo.Verify(repo => repo.GetAll(), Times.Once);
    Assert.Equal(expectedData, testableModel.AllItems);
}

新手测试小贴士

  • 优先测试公共方法的行为:很多时候不需要直接测保护方法,通过调用公共方法(比如GetTableColumnInformation)间接验证内部逻辑;
  • 用简单测试实体:创建极简的TestItem类(只有必要属性)代替真实业务实体,避免依赖复杂逻辑;
  • 先测核心逻辑:优先覆盖分页、过滤、数据更新这些核心功能,再补充边缘场景。

内容的提问来源于stack exchange,提问作者Dane Brazinski

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 14:41:34