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

C#中使用MOQ框架Mock Queue对象报错的原因及解决方法

问题:使用Moq模拟Queue时抛出NotSupportedException异常

尝试用Moq框架Mock Queue<ISampleData> 对象时,出现以下异常:

System.NotSupportedException: 'Unsupported expression: x => x.Enqueue(It.IsAny()) Non-overridable members (here: Queue.Enqueue) may not be used in setup / verification expressions.'

业务代码

public class ISampleData
{
    int ID { get; set; }

    double Data { get; set; }
}

public class SampleData: ISampleData
{
    public int ID { get; set; }

    public double Data { get; set; }
}

public class DataManager
{
    private Queue<ISampleData> dataQueue;

    public DataManager(Queue<ISampleData> dataQueue)
    {
        this.dataQueue = dataQueue;
    }

    public bool InsertData(ISampleData sampleData)
    {
        bool flag = false;
        try
        {
            this.dataQueue.Enqueue(sampleData);
            flag = true;
        }
        catch(Exception ex)
        {
            flag = false;
        }

        return flag;
    }
}

测试代码

/// <summary>
/// Testing Enqueue method with exception.
/// </summary>
[Fact]
public void Test_EnqueueMethod_With_Exception()
{
    // Arrange
    Mock<Queue<ISampleData>> mockQueue = new Mock<Queue<ISampleData>>();
    DataManager dataManager = new DataManager(mockQueue.Object);

    mockQueue.Setup(x => x.Enqueue(It.IsAny<ISampleData>())).Throws(new Exception());

    // Act
    var actual = dataManager.InsertData(new SampleData() { ID = 1001, Data = 20.9876 });

    // Assert
    Assert.False(actual);
}

报错原因

  1. Moq核心限制:Moq的工作逻辑是创建目标类型的派生类,通过重写方法实现Mock。但Queue<T>是密封类(sealed),且Enqueue方法是非虚方法,无法被重写,因此Moq无法对该方法进行Setup或验证操作。
  2. 业务代码小问题:ISampleData被定义为class而非interface,导致SampleData并未正确实现其成员(ISampleData的属性为私有,SampleData的为公共,实际未达成接口实现)。

解决方法

方案1:抽象队列行为(推荐)

通过定义接口封装队列操作,让DataManager依赖抽象而非具体实现,彻底解决Moq无法Mock密封类的问题。

  1. 定义队列操作接口:
public interface IDataQueue
{
    void Enqueue(ISampleData item);
    // 可按需添加Dequeue、Count等其他队列方法
}
  1. 实现接口的包装类:
public class DataQueue : IDataQueue
{
    private readonly Queue<ISampleData> _innerQueue = new Queue<ISampleData>();

    public void Enqueue(ISampleData item)
    {
        _innerQueue.Enqueue(item);
    }
}
  1. 修正ISampleData为接口:
public interface ISampleData
{
    int ID { get; set; }
    double Data { get; set; }
}

public class SampleData : ISampleData
{
    public int ID { get; set; }
    public double Data { get; set; }
}
  1. 修改DataManager依赖抽象:
public class DataManager
{
    private readonly IDataQueue _dataQueue;

    public DataManager(IDataQueue dataQueue)
    {
        _dataQueue = dataQueue;
    }

    public bool InsertData(ISampleData sampleData)
    {
        try
        {
            _dataQueue.Enqueue(sampleData);
            return true;
        }
        catch(Exception)
        {
            return false;
        }
    }
}
  1. 更新测试代码:
[Fact]
public void Test_EnqueueMethod_With_Exception()
{
    // Arrange
    var mockQueue = new Mock<IDataQueue>();
    var dataManager = new DataManager(mockQueue.Object);

    mockQueue.Setup(x => x.Enqueue(It.IsAny<ISampleData>())).Throws(new Exception());

    // Act
    var actual = dataManager.InsertData(new SampleData() { ID = 1001, Data = 20.9876 });

    // Assert
    Assert.False(actual);
}

方案2:使用真实队列模拟异常(局限场景)

如果不想修改业务依赖,可利用真实队列,但仅能模拟特定场景的异常(比如传入会抛出异常的对象):

[Fact]
public void Test_EnqueueMethod_With_Exception()
{
    // Arrange
    var realQueue = new Queue<ISampleData>();
    var dataManager = new DataManager(realQueue);
    // 自定义一个访问属性时会抛出异常的SampleData
    var badData = new BadSampleData();

    // Act
    var actual = dataManager.InsertData(badData);

    // Assert
    Assert.False(actual);
}

public class BadSampleData : ISampleData
{
    public int ID 
    { 
        get => throw new Exception();
        set {} 
    }
    public double Data { get; set; }
}

这种方法仅适用于队列操作会触发对象成员访问的场景,通用性差,不推荐作为常规方案。

内容的提问来源于stack exchange,提问作者The Kingmaker

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 22:13:12