Kamil Stadryniak
Kamil Stadryniak

Reputation: 632

How to short-circuit invocation of property setter in Moq

I've recently got a small headache during unit testing one of my property setters. I wanted to setup my property to return a certain value and do not invoke setter logic because it has some heavy operations in there and I don't want that logic to affect my unit test.

I know I can move that logic to a method and then mock that new method instead but that problem made me curious and I've dug a little. The results of my research is in FooTests class below, one of them using SetupProperty works, but makes me feel that this is not what this method is written for.

Is there a dedicated way to short-circuit setters in partial mocks in Moq?

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

Upvotes: 1

Views: 1506

Answers (1)

KozhevnikovDmitry
KozhevnikovDmitry

Reputation: 1720

The difference in results of the tests is caused by different behavior of Mock, that was configured. Method Setup In the first test just override getter method:

Specifies a setup on the mocked type for a call to a value returning method.

So in this case call of FooBar in constructor affects MagicNumber. Overloading of method SetupSet you used in second test is obsolete, and looks like it doesn't override the setter, it just set up an expectation, that you can verify later? on or add a callback:

Specifies a setup on the mocked type for a call to to a property setter, regardless of its value.

In this case FooBar in constructor affects the MagicNumber too. However FooBar setter is called twice: from constructor and from lambda, where it called with return value of It.IsAny<int>, which is 0. At last, SetupProperty from the third test sets up default property behavior:

Specifies that given property should have a property behavior, meaning that setting its value will cause it to be saved and later returned when the property is requested(this is also known as stubbing)

So FooBar in constructor doesn't affect MagicNumber in the third test, because the whole property is covered with stub, and you never get to FooBar setter. Therefore the third test is green. I suppose that configuration, that you implemented in third test, do what you need. You can combine it with the first one to make FooBar getter always return the same value:

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

Hope it helps.

Upvotes: 1

Related Questions