如何在Moq中短路属性设置器的调用

时间:2018-09-17 16:02:13

标签: c# unit-testing nunit moq

最近,在对我的一位资产设定者进行单元测试时,我有些头疼。我想将属性设置为返回某个值,并且不调用设置器逻辑,因为其中存在大量繁琐的操作,并且我不希望该逻辑影响我的单元测试。

我知道我可以将逻辑移到方法上,然后模拟该新方法,但是这个问题使我感到好奇,并且我已经做了一些工作。 我的研究结果在下面的FooTests类中,其中之一使用SetupProperty起作用,但让我感到这不是此方法的目的。

在Moq中,是否有一种专门的方法可以使Sets短路?

Foo.cs:

public class Foo
{
    private int _bar;
    public virtual int Bar
    {
        get => _bar;
        set
        {
            MagicNumber+=FooBar;
            _bar = value;
        } 
    }

    private int _fooBar;
    public virtual int FooBar
    {
        get => _fooBar;
        set
        {
            //Complex and heavy logic that makes the magic number -value
            MagicNumber = -value;
            _fooBar = value;
        }
    }

    public int MagicNumber { get; set; }

    public Foo()
    {
        FooBar = 1;
    }
}

FooTests.cs:

[TestFixture]
public class FooTests
{
    //Using ordinary setup.
    [TestCase(1, 2, 2, TestName = "BarSetter_BarSetToOneAndFooBarEqualsTwo_MagicNumberEqualsTwo")]
    public void BarSetterTest(int bar, int fooBar, int expectedMagicNumber)
    {
        var fooPartialMock = new Mock<Foo> {CallBase = true};
        fooPartialMock.Setup(x => x.FooBar).Returns(fooBar);
        fooPartialMock.Object.Bar = bar;
        Assert.AreEqual(expectedMagicNumber, fooPartialMock.Object.MagicNumber);
    }

    //Using callbacks.
    [TestCase(1, 2, 2, TestName = "BarSetter_BarSetToOneAndFooBarEqualsTwo_MagicNumberEqualsTwo2")]
    public void BarSetterTest2(int bar, int fooBar, int expectedMagicNumber)
    {
        var fooPartialMock = new Mock<Foo> { CallBase = true };
        fooPartialMock.SetupSet(x => x.FooBar = It.IsAny<int>()).Callback<int>(x => {});
        fooPartialMock.Object.Bar = bar;
        Assert.AreEqual(expectedMagicNumber, fooPartialMock.Object.MagicNumber);
    }

    //Using SetupProperty.
    [TestCase(1, 2, 2, TestName = "BarSetter_BarSetToOneAndFooBarEqualsTwo_MagicNumberEqualsTwo3")]
    public void BarSetterTest3(int bar, int fooBar, int expectedMagicNumber)
    {
        var fooPartialMock = new Mock<Foo> { CallBase = true };
        fooPartialMock.SetupProperty(x => x.FooBar);
        fooPartialMock.Object.FooBar = fooBar;
        fooPartialMock.Object.Bar = bar;
        Assert.AreEqual(expectedMagicNumber, fooPartialMock.Object.MagicNumber);
    }
}

1 个答案:

答案 0 :(得分:1)

测试结果的差异是由配置的Mock的不同行为引起的。方法Setup在第一个测试中,只需重写getter方法:

  

指定用于调用值返回方法的模拟类型的设置。

因此,在这种情况下,在构造函数中调用FooBar会影响MagicNumber。您在第二次测试中使用的方法SetupSet的重载已过时,并且看起来它没有覆盖setter,只是建立了一个期望值,您以后可以验证吗?或添加回调:

  

在模拟类型上指定用于调用属性设置器的设置,而不管其值如何。

在这种情况下,构造函数中的FooBar也会影响MagicNumber。但是FooBar的setter被调用了两次:从构造函数和lambda调用,在此它返回的返回值It.IsAny<int>为0。最后,第三个测试的SetupProperty设置了默认属性行为:

  

指定给定属性应具有property behavior,这意味着设置其值将导致该属性被保存并在请求该属性时返回(也称为存根)

因此,构造函数中的FooBar不会在第三个测试中影响MagicNumber,因为整个属性都被存根覆盖了,而且您再也不会遇到FooBar setter了。因此,第三个测试是绿色的。我想您在第三次测试中实现的配置可以满足您的需求。您可以将其与第一个结合使用,以使FooBar getter始终返回相同的值:

fooPartialMock.SetupProperty(x => x.FooBar).Setup(x => x.FooBar).Returns(fooBar);

希望有帮助。