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

如何在Moq回调方法中使用模拟对象并验证其属性值?

问题描述

我正在模拟如下接口:

public interface IVirtualFile
{
    string Contents { get; set; }
    // ...
    void SaveToDisk();
    // ...
}

用于测试以下类:

public class InformationHandler
{
    // ...
    public void PrepareAndSaveSpecificInfo(IVirtualFile p_objSaveFile)
    {
        // ...
        p_objSaveFile.Contents = z_strBeginning;
        // ...
        p_objSaveFile.Contents = String.Concat(p_objSaveFile.Contents, z_strOtherInformation);
        // ...
        p_objSaveFile.Contents = String.Concat(p_objSaveFile.Contents, z_strEnd);
        p_objSaveFile.SaveToDisk();
    }
    // ...
}

测试代码如下:

[TestFixture]
public class TestInformationHandler
{
    private Mock<IVirtualFile> _mockVirtualFile { get; set; }

    [OneTimeSetUp]
    public void CreateMocks()
    {
        // ...
        _mockVirtualFile = new Mock<IVirtualFile>();

        _mockVirtualFile.Setup(vf => vf.SaveToDisk()).Callback(() => CheckSavedData());
        // ...
    }

    private void CheckSavedData()
    {
        // ...
    }    

    [Test]
    public void Test_PrepareAndSaveSpecificInfo()
    {
        // ...
        PrepareAndSaveSpecificInfo(_mockVirtualFile);
        // ...
    }    
}

遇到两个问题:

  • 希望在CheckSavedData方法中通过Assert验证模拟对象的Contents属性,但不清楚如何获取该模拟对象;
  • 调试时设置_mockVirtualFile.Object.Contents = "abcdef"后,属性仍返回null,模拟对象的属性不保留设置的值。

解决方案

1. 解决模拟属性不保留值的问题

Moq默认不会自动跟踪属性的get和set行为,需要显式配置属性的存储逻辑,有两种方式可选:

方式一:单独配置指定属性

只针对Contents属性开启值保留:

[OneTimeSetUp]
public void CreateMocks()
{
    _mockVirtualFile = new Mock<IVirtualFile>();
    // 配置Contents属性允许get/set并保留值
    _mockVirtualFile.SetupProperty(vf => vf.Contents);
    _mockVirtualFile.Setup(vf => vf.SaveToDisk()).Callback(() => CheckSavedData());
}

方式二:配置所有属性自动跟踪

如果接口有多个需要保留值的属性,可一次性开启所有属性的跟踪:

[OneTimeSetUp]
public void CreateMocks()
{
    _mockVirtualFile = new Mock<IVirtualFile>();
    // 自动跟踪所有属性的get/set行为
    _mockVirtualFile.SetupAllProperties();
    _mockVirtualFile.Setup(vf => vf.SaveToDisk()).Callback(() => CheckSavedData());
}

2. 在CheckSavedData中验证Contents属性

由于_mockVirtualFile已经是测试类的私有属性,直接在CheckSavedData方法中使用即可,无需额外传入。同时要修正测试方法的参数错误:需要传入模拟对象的实例(_mockVirtualFile.Object),而非Mock本身。

修正后的完整测试代码:

[TestFixture]
public class TestInformationHandler
{
    private Mock<IVirtualFile> _mockVirtualFile { get; set; }
    // 替换为实际测试用的预期值
    private string z_strBeginning = "Prefix: ";
    private string z_strOtherInformation = "Sample Content";
    private string z_strEnd = " :Suffix";

    [OneTimeSetUp]
    public void CreateMocks()
    {
        _mockVirtualFile = new Mock<IVirtualFile>();
        _mockVirtualFile.SetupProperty(vf => vf.Contents);
        _mockVirtualFile.Setup(vf => vf.SaveToDisk()).Callback(() => CheckSavedData());
    }

    private void CheckSavedData()
    {
        // 构建预期的拼接结果
        string expectedContent = z_strBeginning + z_strOtherInformation + z_strEnd;
        // 验证Contents属性是否符合预期
        Assert.That(_mockVirtualFile.Object.Contents, Is.EqualTo(expectedContent));
    }    

    [Test]
    public void Test_PrepareAndSaveSpecificInfo()
    {
        var handler = new InformationHandler();
        // 传入模拟对象的实例,而非Mock本身
        handler.PrepareAndSaveSpecificInfo(_mockVirtualFile.Object);
        // 可选:验证SaveToDisk方法是否被调用一次
        _mockVirtualFile.Verify(vf => vf.SaveToDisk(), Times.Once);
    }    
}

内容的提问来源于stack exchange,提问作者Jean-David Lanz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 20:37:14