【问题标题】:How to mock a method call that takes a dynamic object如何模拟采用动态对象的方法调用
【发布时间】:2025-12-19 06:50:11
【问题描述】:

假设我有以下内容:

public interface ISession 
{
   T Get<T>(dynamic filter); }
}

我有以下代码要测试:

var user1 = session.Get<User>(new {Name = "test 1"});
var user2 = session.Get<User>(new {Name = "test 2"});

如何模拟这个调用?

使用起订量,我厌倦了这样做:

var sessionMock = new Mock<ISession>();
sessionMock.Setup(x => x.Get<User>(new {Name = "test 1")).Returns(new User{Id = 1});
sessionMock.Setup(x => x.Get<User>(new {Name = "test 1")).Returns(new User{Id = 2});

那没有用。返回结果为空

我还尝试使用 Rhino Mocks 执行以下操作:

var session = MockRepository.GenerateStub<ISession>();
session.Stub(x => x.Get<User>(new {Name = "test 1"})).Return(new User{Id=1});

也没有运气。再次为空。

那我该怎么做呢?

谢谢,

【问题讨论】:

    标签: c# mocking moq rhino-mocks


    【解决方案1】:

    您可以将It.Is&lt;object&gt; 匹配器与反射一起使用。您不能在表达式树中使用动态,因此 It.Is&lt;dynamic&gt; 将无法正常工作,这就是您需要反射以按名称获取属性值的原因:

    sessionMock
        .Setup(x => x.Get<User>(
            It.Is<object>(d => d.GetPropertyValue<string>("Name") == "test 1")))
        .Returns(new User{Id = 1});
    sessionMock
        .Setup(x => x.Get<User>(
            It.Is<object>(d => d.GetPropertyValue<string>("Name") == "test 2")))
        .Returns(new User { Id = 2 });
    

    GetPropertyValue 是个小帮手:

    public static class ReflectionExtensions
    {
        public static T GetPropertyValue<T>(this object obj, string propertyName)
        {
            return (T) obj.GetType().GetProperty(propertyName).GetValue(obj, null);
        }
    }
    

    【讨论】:

      【解决方案2】:

      Moq 为该案例提供了It.IsAny&lt;T&gt;

      sessionMock.Setup(x => x.Get<User>(It.IsAny<object>()).Returns(new User());
      

      *动态是任何对象

      【讨论】:

      • 感谢 ai.farfa。我不能这样做,因为我需要根据动态对象上的不同属性来判断要返回哪个对象
      【解决方案3】:

      首先,匿名对象并不是真正的dynamic

      如果你使用了dynamic 之类的对象

      dynamic user1Filter = new ExpandoObject();
      user1Filter.Name = "test 1";
      var user1 = session.Get<User>(user1Filter);
      

      你可以嘲笑它

      sessionMock.Setup(x => x.Get<User>(DynamicFilter.HasName("test 1")));
      

      通过实现自定义参数匹配器:

      static class DynamicFilter
      {
          [Matcher] public static object HasName(string name) { return null; }
          public static bool HasName(dynamic filter, string name)
          {
              string passedName = filter.Name; //dynamic expression
              return name.Equals(passedName);
          }
      }
      

      【讨论】: