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

时间:2019-05-02 13:50:38

标签: c# mocking moq

我想创建一个对象的模拟对象,该对象返回一个属性的值来自同一模拟实例的另一个属性,但是我找不到如何使用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;
}

感谢您的建议

2 个答案:

答案 0 :(得分:1)

您可以使用Callback存储来自set的值,然后在Returns中重新使用它。像这样:

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);

另一个选择是实际实施并使用CallBase功能。 (类似于answer

public abstract class MyObjectDummy : IMyObject
{
    public virtual int Id { get; set; }  
    public virtual string PropA { get; set; }
}

var mock = new Mock<MyObjectDummy>();
mock.SetupSet(m => m.PropA = It.IsAny<string>()).CallBase();
mock.SetupGet(m => m.PropA).CallBase();
mock.Setup(m => m.Id).Returns(() => (mock.Object.PropA?.GetHashCode()).GetValueOrDefault());

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

答案 1 :(得分:1)

有时Moq设置很难阅读,经常到其他人很难说出正在测试什么的程度。在这些情况下,我们经常可以使用测试double(即实现接口的类)来“模拟”。

这是一个例子。我不能肯定地说这是否可以解决您的模拟问题,但这确实是重点。阅读该设置并告诉其功能并不容易。另一方面,很容易知道它的作用:

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

    public string PropA { get; set; }
} 

...,然后创建执行相同操作的Mock<IObject>容易得多。

当创建测试双精度型的实例时,类的名称可以清楚地指示该测试双精度型针对特定测试的目的。 (为公平起见,您也可以使用Moq来实现这一点,方法是创建一个命名函数,该函数创建并返回该模拟。)

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

我不建议我们不使用Moq-我一直都在使用它-只是当它变得不必要地复杂时,我们应该研究替代方法。