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

如何Mock INodeQuery.GetNodes()使任意表达式返回固定Fixture值?

问题:Mock INodeQuery.GetNodes()返回固定Fixture值失败

我需要对NodeProcessService进行Mock测试,已定义以下接口与类:

public interface INode {
   public int Id { get; set; }
   public string NodeName { get; set; }
   public IEnumerable<INode> Childrens { get; set; }
}
    
public interface INodeQuery {
   public IEnumerable<INode> GetNodes();
}
public class NodeProcessService {
   readonly INodeQuery nodeQuery;
    
   public NodeProcessService(INodeQuery nodeQuery) {
       this.nodeQuery = nodeQuery;
   }
   public string currentNodeName { get;set; }

        
   public int Handler() {
       var getNode = nodeQuery.GetNodes()
                .Select(p => p.Childrens.FirstOrDefault(c => c.NodeName == currentNodeName))
                .FirstOrDefault(p => p != null);
    //do something...
   }
}

测试时希望Mock INodeQuery,让GetNodes()无论后续使用何种表达式,都返回单个固定的Fixture值,但以下两种Setup写法均报错:

nodeQuery.Setup(s => s.GetNodes().FirstOrDefault(It.IsAny<Func<INode, bool>>())).Returns(fakeNode)

或

nodeQuery.Setup(s => s.GetNodes().FirstOrDefault()).Returns(fakeNode)

测试代码结构如下:

public NodeServiceTests()
{
   fixture.Customize(new AutoMoqCustomization());
   nodeQuery = fixture.Freeze<Mock<INodeQuery>>();
   sut = fixture.Create<NodeProcessService>();
}

[Fact]
public void SimpleTest()
{
    var fakeNode = fixture.Create<INode>();
    nodeQuery.Setup(s => s.GetNodes().FirstOrDefault(It.IsAny<Func<INode, bool>>())).Returns(fakeNode);
    
    var result = sut.Handler();
}

解决方案

错误原因

你试图Mock的FirstOrDefault是Linq扩展方法,并非INodeQuery接口定义的方法,Moq无法直接Mock链式调用中的扩展方法。正确的做法是直接MockINodeQuery.GetNodes()方法,让它返回一个符合业务代码Linq筛选条件的集合,从而确保后续操作能得到你想要的固定节点。

正确实现方式

根据业务代码逻辑,Handler()会遍历GetNodes()返回的节点的子节点,找到名称匹配currentNodeName的节点。我们只需要构造对应的集合并MockGetNodes()返回它即可:

[Fact]
public void SimpleTest()
{
    // 1. 创建要返回的固定子节点,并设置匹配的名称
    var targetChildNode = fixture.Create<INode>();
    var targetNodeName = fixture.Create<string>();
    targetChildNode.NodeName = targetNodeName;
    sut.currentNodeName = targetNodeName;

    // 2. 创建父节点,将固定子节点加入其Childrens集合
    var parentNode = fixture.Create<INode>();
    parentNode.Childrens = new List<INode> { targetChildNode };

    // 3. Mock GetNodes()返回包含该父节点的集合
    nodeQuery.Setup(s => s.GetNodes()).Returns(new List<INode> { parentNode });

    var result = sut.Handler();

    // 此处添加你的断言逻辑,比如验证result是否符合预期
}

简化场景(强制返回固定节点)

如果希望完全忽略业务代码的筛选逻辑,直接让Handler()拿到固定节点,可以构造一个让Linq操作必然返回该节点的集合:

[Fact]
public void SimpleTest()
{
    var fakeNode = fixture.Create<INode>();
    // 设置currentNodeName匹配fakeNode的名称
    sut.currentNodeName = fakeNode.NodeName;

    // 创建一个父节点,其Childrens直接包含fakeNode
    var parentNode = fixture.Build<INode>()
                            .With(n => n.Childrens, new List<INode> { fakeNode })
                            .Create();

    nodeQuery.Setup(s => s.GetNodes()).Returns(new List<INode> { parentNode });

    var result = sut.Handler();
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 06:54:22