Constraint for value within range

41 views
Skip to first unread message

phil....@btinternet.com

unread,
Oct 4, 2013, 7:13:29 AM10/4/13
to rhino...@googlegroups.com
I'm a beginner both with Rhino mocks and mocking in general.  My unit under test does numerical computation and its results are inexact.  Therefore my expectations have to be of the form 'parameter is within a range'.  In trying to achieve this, I've discovered some issues with Rhino that I don't know how to resolve.

Here's a simple test, which passes.

        [Test]
        public void Simple()
        {
            // Arrange
            ICall call = MockRepository.GenerateStrictMock<ICall>();
            for (int x = 0; x < 1000; x += 100)
            {
                call.Expect(c => c.Call(x));
            }

            // Act
            for (int x = 900; x >= 0; x -= 100)
            {
                call.Call(x);
            }

            // Assert
            call.VerifyAllExpectations();
        }

If I change the expectation to use the Arg approach, then this also passes:

                call.Expect(c => c.Call(Arg<int>.Is.Equal(x)));

If I use the Matches approach to define a custom constraint, however, the test fails:

                call.Expect(c => c.Call(Arg<int>.Matches(p => p == x)));

AnalysisUnitTests.TrackFinderTests.Arg_Matches_x:
Rhino.Mocks.Exceptions.ExpectationViolationException : ICall.Call(900); Expected #0, Actual #1.
ICall.Call(p => (p = value(AnalysisUnitTests.TrackFinderTests+<>c__DisplayClass24).x)); Expected #1, Actual #0.

I presume that this is because the predicate (including x) is evaluated as the matching takes place - in which case x is either undefined or fixed in all instances of the predicate.

The reason I was trying to use .Matches is of course that I wanted to write something like 

    .Arg<int>.Matches(p => p > x - tolerance && p < x + tolerance)

Is there another way to achieve this?  Clearly I can do part of the range checking:

    .Arg<int>.Is.GreaterThan(x - tolerance)

So is there a way to 'chain' the constraints?  Something like 

    .Arg<int>.Is.GreaterThan(x - tolerance).AndIs.LessThan(x + tolerance)

Any help gratefully received!

phil....@btinternet.com

unread,
Oct 4, 2013, 12:32:05 PM10/4/13
to rhino...@googlegroups.com
I found a way to do this by creating a RangeConstraint class as a subclass of AbstractConstraint, although I'd still be interested to know if there is a more straightforward way.

Reginald Blue

unread,
Oct 4, 2013, 12:50:50 PM10/4/13
to rhino...@googlegroups.com
I looked, I think that is the most straightforward way that I could see.  You could ask to add your "RangeConstraint" to the next version... though I think I'd recommend "Between" for it's name instead, but that's just me.


On Fri, Oct 4, 2013 at 12:32 PM, <phil....@btinternet.com> wrote:
I found a way to do this by creating a RangeConstraint class as a subclass of AbstractConstraint, although I'd still be interested to know if there is a more straightforward way.

--
You received this message because you are subscribed to the Google Groups "Rhino.Mocks" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rhinomocks+...@googlegroups.com.
To post to this group, send email to rhino...@googlegroups.com.
Visit this group at http://groups.google.com/group/rhinomocks.
For more options, visit https://groups.google.com/groups/opt_out.

Phil Atkin

unread,
Oct 4, 2013, 1:03:50 PM10/4/13
to rhino...@googlegroups.com
Sometimes it's better to specify the limits, in which case Between is a good name (although it fails to make clear whether the range is inclusive or exclusive of the limits themselves), but quite often it's easier to specify a central value and a tolerance either side of it - which is what I wanted here.
You received this message because you are subscribed to a topic in the Google Groups "Rhino.Mocks" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/rhinomocks/_WoOlhrdaAg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to rhinomocks+...@googlegroups.com.

Alexander Fedin

unread,
Oct 5, 2013, 4:13:46 AM10/5/13
to rhino...@googlegroups.com
It fails because of the bug in the Rhino Mocks code: the code tries to compile the expression by calling LambdaExpression.Compile method through the reflection and does not take in account that there are two methods with the same name on that class. Somebody should fix this annoying thing.

Phil Atkin

unread,
Oct 5, 2013, 7:57:35 AM10/5/13
to rhino...@googlegroups.com
Sorry - which two methods on what class? Are you saying that the
behaviour of my examples would be different if the bug were fixed?

haifisch

unread,
Oct 7, 2013, 2:33:00 AM10/7/13
to rhino...@googlegroups.com
Hi,

this is default C# behavior handling closures.

Just change your loop to store a local copy of the variable x and the test will run.

      for (int x = 0; x < 1000; x += 100)
      {
        int x1 = x;
        call.Expect(c => c.Call(Arg<int>.Matches(p => p == x1)));
      }

As far as I can see there is no bug in RhinoMocks.
But if anyone might want to further improve the API for this kind of constraints - contributions to https://github.com/hibernating-rhinos/rhino-mocks/network or any other more advanced branch are always welcome.

Best regards,

Andreas

Phil Atkin

unread,
Oct 7, 2013, 6:08:23 AM10/7/13
to rhino...@googlegroups.com
Excellent; the best solutions are those that seem obvious once they're pointed out.

For my sort of (numerical applications), I'd still appreciate an easy way to test for value-within-tolerance, but with this information I can certainly solve my immediate problem.

Thanks,

Phil
Reply all
Reply to author
Forward
0 new messages