C# 模拟对象的属性以返回来自对象本身的值

C# 模拟对象的属性以返回来自对象本身的值,c#,mocking,moq,C#,Mocking,Moq,我想创建一个mock,其中一个对象为一个属性返回来自同一mock实例的另一个属性的值,但我不知道如何使用Moq设置它 我为Setup/SetupGet找到的所有签名都只允许返回常量值,而不允许返回来自同一对象的值 例如: void Main() { var obj = GetMock().Object; obj.PropA = "test"; Console.WriteLine(obj.Id); // Should return -354185609, as it's

我想创建一个mock,其中一个对象为一个属性返回来自同一mock实例的另一个属性的值,但我不知道如何使用Moq设置它

我为Setup/SetupGet找到的所有签名都只允许返回常量值,而不允许返回来自同一对象的值

例如:

void Main()
{
    var obj = GetMock().Object;

    obj.PropA = "test";

    Console.WriteLine(obj.Id); // Should return -354185609, as it's the result of "test".GetHashCode()

    obj.PropA = "test 2";

    Console.WriteLine(obj.Id); // Should return -1555281747, as it's the result of "test 2".GetHashCode()

}

public interface IMyObject
{
    int Id { get;set;}

    string PropA {get; set;}
}

public Mock<IMyObject> GetMock()
{
    var objectMock = new Mock<IMyObject>();

    objectMock.SetupAllProperties();

    objectMock
        .SetupGet((IMyObject s) => s.Id)
        //Here I would like to write something like : 
        .Returns(s => (a.PropA?.GetHashCode()).GetValueOrDefault()));
        // To have the Getter of Id initialized with the current value of PropA.GetHashCode()

    return objectMock;
}
void Main()
{
var obj=GetMock().Object;
obj.PropA=“测试”;
Console.WriteLine(obj.Id);//应该返回-354185609,因为它是“test”的结果
obj.PropA=“测试2”;
Console.WriteLine(obj.Id);//应该返回-1555281747,因为它是“test2”的结果
}
公共接口IMyObject
{
int Id{get;set;}
字符串PropA{get;set;}
}
公共Mock GetMock()
{
var objectMock=new Mock();
objectMock.SetupAllProperties();
对象模拟
.SetupGet((IMyObject s)=>s.Id)
//在这里,我想写一些类似于:
.Returns(s=>(a.PropA?.GetHashCode()).GetValueOrDefault());
//使用当前值PropA.GetHashCode()初始化Id的Getter
返回对象模拟;
}

感谢您的建议

您可以使用
回调
存储
集合
中的值,然后在
返回
中重新使用它。大概是这样的:

string propA = null;

var mock = new Mock<IMyObject>();
mock.SetupSet(m => m.PropA = It.IsAny<string>())
    .Callback<string>(s => propA = s);
mock.Setup(m => m.Id)
    .Returns(() => (propA?.GetHashCode()).GetValueOrDefault());

mock.Object.PropA = "test";

Assert.Equal("test".GetHashCode(), mock.Object.Id);

mock.Object.PropA = "test 2";

Assert.Equal("test 2".GetHashCode(), mock.Object.Id);

有时,Moq设置变得很难阅读,通常会导致其他人很难说出正在测试的内容。在这些场景中,我们通常可以使用双重测试来“模拟”——也就是说,一个实现接口的类

这里有一个例子。我不能肯定这是否和你的模拟一样,但这才是真正的重点。阅读该设置并说出它的作用并不容易。另一方面,很容易看出它的作用:

public class ObjectWhereIdReturnsPropAHashCode : IMyObject
{
    public int Id
    {
        get => PropA?.GetHashCode() ?? 0;
        set => Id = value;
    }

    public string PropA { get; set; }
} 
…然后创建一个做同样事情的
Mock
就容易多了

当您创建test double的实例时,类的名称可以清楚地指示该test double为该特定测试所做的工作。(公平地说,您也可以使用Moq创建并返回模拟的命名函数来实现这一点。)

对于下一个开发人员来说,这也是一个更容易遵循的模式,他们必须创建一个新的双重测试或修改现有的双重测试


我并不是说我们不应该使用Moq——我一直都在使用它——只是说,当它变得不必要的复杂时,我们应该寻找替代品。

谢谢,它是有效的!然而,由于我还需要使用提供的值实际分配“PropA”setter,我刚刚更新以重新分配它:settingDefinitionMock.SetupGet(m=>m.SettingKey).Returns(()=>SettingKey);谢谢。为了完整起见,是否有某种语法类似于我在第一篇文章中的示例,可以直接访问其配置中的模拟实例并访问其中实现的属性(或方法),或者我们必须使用谓词捕获的外部变量的语法,而这不是很容易理解的吗?@AFract您可以使用
CallBase
功能,但是您需要真正的实现,使用接口这就是方法。您好。老实说,我在第一篇文章中遇到的问题,用真正实现的类来解决要比用一个动态模拟来解决要简单得多,正如您所示,我知道这一点,但我非常有兴趣发现如何用Moq来处理这种情况。第一篇文章中的示例并没有涵盖我对mock的所有需求。这很有趣,因为我喜欢知道这些东西是如何工作的,但我在一些项目中遇到过mock失控的情况——它设置一个mock来做一些复杂的事情,然后设置另一个mock返回第一个mock。诚然,其中一些是其他代码问题的影响。但是当它达到那个点时,我的眼睛变得呆滞,我再也说不出发生了什么。我同意不要过度使用复杂的模拟,但是知道如何处理某些特殊情况仍然很有趣:)。我发现我的问题很有趣,但我无法用Moq的方便语法来解决它。
public class ObjectWhereIdReturnsPropAHashCode : IMyObject
{
    public int Id
    {
        get => PropA?.GetHashCode() ?? 0;
        set => Id = value;
    }

    public string PropA { get; set; }
}