3
\$\begingroup\$

I'm trying to use Moq to mock a rather large set of interfaces in an object. I'm trying to capture the behavior of retaining a dirty flag when properties are set and I'm hoping there is a cleaner way to do this because I have many properties that need this setup.

    public static void SetupDirtyProperty<T, TProp>(this Mock<T> mock, Expression<Func<T, TProp>> getterExpression, Action<T> setterExpression, ref bool isDirty)
    {
        TProp p = default(TProp);

        mock.SetupGet(getterExpression).Returns(() => p);
        mock.SetupSet(setterExpression).Callback((TProp value) =>
        {
            if (p != value)
            {
                p = value;
                isDirty = true;
            }
        });
    }

and to use it it's called like this:

        bool isDirty = false;
        var product = new Mock<IProduct>();

        product.As<ISku>().SetupDirtyProperty(x => x.Price, x => x.Price= It.IsAny<decimal?>(), () => isDirty = true);

The things I would like to simplify about this approach are:

  • I have to specify a property getter and setter, but it seems like I should be able to invert the property getter expression into a setter with It.IsAny<TProp> that will work correctly with Moq.
  • I have to specify the closure around the isDirty flag every time.

It'd be nice if I could set this up in a way that is easy to apply this to dozens of properties, and it'd be really nice if it was something like SetupAllProperties where it would just handle all the properties automatically.

\$\endgroup\$
6
  • \$\begingroup\$ This code appears to be hypothetical and is not really a code review, but asking for us to make your code do what you want, but I'm not really sure. What are you trying to achieve with this dirty flag? Just check whether or not a property was changed? \$\endgroup\$
    – Dan
    Mar 7 '15 at 0:09
  • \$\begingroup\$ This code is operational, I would just like to simplify the syntax on the call. The code I'm trying to get under test sets some properties and then makes decisions based on whether the dirty flag is set. I would like to write tests that exercise that code. \$\endgroup\$
    – Erik
    Mar 7 '15 at 0:18
  • 1
    \$\begingroup\$ Consider using a different pattern like the observer pattern? It is designed to solve this problem. \$\endgroup\$
    – Dan
    Mar 7 '15 at 0:19
  • \$\begingroup\$ The code I'm trying to get under test is legacy code and I don't want to make any changes to it until I get some good unit test coverage. Besides, I don't think the observer pattern would be appropriate here, because the code doesn't need a notification on every property changed, it just wants to inspect whether anything has changed afterwards to determine whether the data needs to be updated in the persistent storage. \$\endgroup\$
    – Erik
    Mar 7 '15 at 0:27
  • \$\begingroup\$ That's exactly what the observer pattern does, though? O.o \$\endgroup\$
    – Dan
    Mar 7 '15 at 0:30
1
\$\begingroup\$

Keeping code DRY is a noble goal, even for test code, but it's less important for tests. Our first priority when writing test code is being precise and clear. The tests are the specs and the specs are the tests. It's important that it be immediately clear what the state of the mocked object is. We can expect to duplicate a certain amount of code.

Looking at your test, in isolation from your extension, it's difficult to tell exactly what is going on here.

bool isDirty = false;
var product = new Mock<IProduct>();

product.As<ISku>().SetupDirtyProperty(x => x.Price, x => x.Price= It.IsAny<decimal?>(), () => isDirty = true);

If you need to create this same Moq for many tests, it would be better to use the [TestInitialize] attribute and have it set up prior to each test method being run. See this for more information.

\$\endgroup\$
2
  • \$\begingroup\$ Yes, this is code which would be called from a [TestInitialize] method. \$\endgroup\$
    – Erik
    Mar 14 '15 at 13:03
  • \$\begingroup\$ Then I'm afraid it's unclear what you're asking @Erik. I would suggest posting more (most) of your test code for a proper review. \$\endgroup\$
    – RubberDuck
    Mar 14 '15 at 13:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.