Search code examples
c#rhino-mocks

Testing a property set to an instance of a new object in Rhino Mocks 3.4.0


Background

I'm fixing unit tests which have been neglected for a long time for legacy code in our organisation. They're written using Rhino Mocks 3.4.0, and I'm struggling to find a way of making this test pass. Rhino Mocks documentation seems to have gone, and most answers here and blogs seem to be using updated 3.5 and 3.6 syntax.

I'm wary of updating the version of Rhino Mocks we're using, as we have several thousand unit tests which may or may not need updated if we update.

The scenario:

We have a Presenter and a View. When the Presenter is initialised, it sets some default filter properties in the View. In the past, both of these properties were enums and the test passed. The last change updated one of the properties to be an instance of a class. The test was updated to expect a call to a static method which creates an instance with default values (matching the code under test), but the test now fails with the error Rhino.Mocks.Exceptions.ExpectationViolationException : Unordered method call.

Some sample code:

    public enum FilterOptions { OptionA, OptionB, OptionC }

    public class OtherFilterOptions
    {
        public bool Filter1 { get; set;}
        public bool Filter2 { get; set; }

        public OtherFilterOptions(bool filter1 = true, bool filter2 = false)
        {
            Filter1 = filter1;
            Filter2 = filter2;
        }

        public static OtherFilterOptions DefaultFilterOptions()
        {
            return new OtherFilterOptions();
        }
    }

    public interface IToTestView
    {
        FilterOptions Property1 { set; }
        OtherFilterOptions Property2 { set; }
    }

    public class ToTestPresenter
    {
        public IToTestView View { get; set; }

        public ToTestPresenter(IToTestView view)
        {
            View = view;
        }

        public void InitialiseView()
        {
            View.Property1 = FilterOptions.OptionA;
            View.Property2 = OtherFilterOptions.DefaultFilterOptions();
        }
    }

And a failing test:

    [TestFixture]
    class Tests
    {
        [Test]
        public void TestOne()
        {
            var mocks = new MockRepository();
            var mockView = mocks.CreateMock<IToTestView>();

            ToTestPresenter presenter = new ToTestPresenter(mockView);
            using (mocks.Ordered())
            {
                mockView.Property1 = FilterOptions.OptionA;
                mockView.Property2 = OtherFilterOptions.DefaultFilterOptions();
            }

            mocks.ReplayAll();
            presenter.InitialiseView();
            mocks.VerifyAll();
        }
    }

The full error is

Rhino.Mocks.Exceptions.ExpectationViolationException : Unordered method call! The expected call is: 'Ordered: { IToTestView.set_Property2(RhinoMocksTestApp.OtherFilterOptions); }' but was: 'IToTestView.set_Property2(RhinoMocksTestApp.OtherFilterOptions);'

I'm assuming that the test is failing because the value to be set is a method call rather than a concrete value. I've tried declaring a variable using mockView.Property2 = theVariable, but there's no change to the error.

Can I set an expectation that Property2 will be set to {some object with Values Filter1 = true, Filter2 = false}? I've seen examples doing similarly using Rhino Mocks 3.6, but is anything available using 3.4.0?

Edit: As an example, this is an example test which passes in Rhino Mocks 3.6.1 - I'm hoping to find some syntax that works similarly for 3.4.0, if it exists.

[Test]
public void TestOne()
{
    var mocks = new MockRepository();
    var mockView = MockRepository.GenerateMock<IToTestView>();
    ToTestPresenter presenter = new ToTestPresenter(mockView);

    mocks.ReplayAll();
    presenter.InitialiseView();

    mockView.AssertWasCalled(v => v.Property1 = FilterOptions.OptionA);
    mockView.AssertWasCalled(v => v.Property2 = Arg<OtherFilterOptions>.Matches(filters => 
        (filters.Filter1 == true) && (filters.Filter2 == false)));
}

Solution

  • The answer I was looking for was in the LastCall.Constraints() method. Passing arguments to Constraints allows you to specify property values of an argument:

    [Test]
    public void TestOne()
    {
        var mocks = new MockRepository();
        var mockView = mocks.CreateMock<IToTestView>();
    
        ToTestPresenter presenter = new ToTestPresenter(mockView);
        using (mocks.Ordered())
        {
             mockView.Property1 = FilterOptions.OptionA;
             mockView.Property2 = OtherFilterOptions.DefaultFilterOptions();
             LastCall.Constraints(
                 Property.Value("Filter1", true)
                 & Property.Value("Filter2", false));
        }
    
        mocks.ReplayAll();
        presenter.InitialiseView();
        mocks.VerifyAll();
    }
    

    There are a large number of options that can be passed in to the Constraints() method. Details on some of them on this CodeProject page

    Another option is LastCall.IgnoreArguments() if you don't care what the property is actually set to.