0

I have this dataAccess mock object and I'm trying to verify that one of its methods is being invoked, and that the argument passed into this method fulfills certain constraints. As best I can tell, this method is indeed being invoked, and with the constraints fulfilled. This line of the test throws a MockException:

data.Verify(d => d.InsertInvoice(It.Is<Invoice>(i => i.TermPaymentAmount == 0m)), Times.Once());

However, removing the constraint and accepting any invoice passes the test:

data.Verify(d => d.InsertInvoice(It.IsAny<Invoice>()), Times.Once());

I've created a test windows form that instantiates this test class, runs its .Setup() method, and then calls the method which I am wishing to test. I insert a breakpoint on the line of code where the mock object is failing the test

data.InsertInvoice(invoice);

to actually hover over the invoice, and I can confirm that its .TermPaymentAmount decimal property is indeed zero at the time the method is invoked.

Out of desperation, I even added a call back to my dataAccess mock:

data.Setup(d => d.InsertInvoice(It.IsAny<Invoice>())).Callback((Invoice inv) => MessageBox.Show(inv.TermPaymentAmount.ToString("G17")));

And this gives me a message box showing 0. This is really baffling me, and no one else in my shop has been able to figure this out. Any help would be appreciated.

A barely related question, which I should probably ask independently, is whether it is preferable to use Mock.Verify() as I have here, or to use Mock.Expect(). Verifiable followed by Mock.VerifyAll() as I have seen other people doing? If the answer is situational, which situations would warrent the use of one over the other?

abatishchev
  • 98,240
  • 88
  • 296
  • 433
  • What version of Moq are you using? – Mark Coleman Apr 15 '10 at 17:10
  • Mark - you caught me running 4.0 beta without realizing it, though I have switched to 3.1.0.0 and am still having the same problem. –  Apr 15 '10 at 17:52
  • I can add that I tried out this out of desperation: data.Setup(d => d.InsertInvoice(It.Is(i => i.TermPaymentAmount != 0))).Throws(new ArgumentException("TermPaymentAmount was not 0!")); and it DOES behave as expected, and does not throw this exception, because the Invoice.TermPaymentAmount is == 0. (And it DOES throw the exception when I change the comparison to != 0.) So this does accomplish the same check I was initially striving for, though I can't figure out why my initial method doesn't work. –  Apr 21 '10 at 18:37
  • Did you get any traction on this? – Merlyn Morgan-Graham Feb 20 '11 at 01:50
  • This particular problem still remains a mystery to me, though I have recently discovered that when I verify that a method on a mock was invoked "Times.Once()" that the exception message that Moq gives me is unclear as to whether the method was invoked zero times or more than once, so I've been intending to reattack this issue when I have a bit of time. –  Feb 21 '11 at 15:05

3 Answers3

0

Just to rule this out (because I've got some strange behavior like you :
Try Times.Exactly(1) instead of Times.Once(). I've had some weird thing happening when using Times.Once() Though, I would have guessed that Once() is using Exactly(1) internally...

Stéphane
  • 11,755
  • 7
  • 49
  • 63
  • This was a good suggestion but in this case it didn't solve the problem. Thanks though! –  Apr 15 '10 at 17:53
0

I am using version 3.1.0.0 of Moq and am not experiencing this issue with the following test case.

internal class Program
{
    private static void Main(string[] args)
    {
        var mock = new Mock<IData>();
        mock.Setup(d => d.InsertInvoice(It.IsAny<Invoice>()));

        var service = new MyService(mock.Object);
        service.DoSomething(new Invoice());

        mock.Verify(d => d.InsertInvoice(It.Is<Invoice>(i => i.TermPaymentAmount == 0m)), Times.Once());

        Console.ReadLine();
    }
}

internal class MyService
{
    private readonly IData _data;

    public MyService(IData data)
    {
        _data = data;
    }

    public void DoSomething(Invoice invoice)
    {
        _data.InsertInvoice(invoice);
    }
}

public class Invoice
{
    public decimal TermPaymentAmount { get; set; }
}

public interface IData
{
    void InsertInvoice(Invoice invoice);
}

Could you supply maybe a bit more information on how you are using the mock?

Mark Coleman
  • 40,542
  • 9
  • 81
  • 101
  • I'm using the mock in almost exactly the same way that you are, Mark. Thanks for taking the time to put this together. I only see some subtle differences that shouldn't matter: 1. I don't call mock.Setup() as you did - I'm using the loose mock behavior and was just trying to verify that the .InsertInvoice() method was invoked. I did call mock.Setup() to add that desperate callback, but that was after this problem arose. 2. There are some other subtle differences (invoice object is created earlier in my code), though I have not yet found anything that should make any difference. –  Apr 15 '10 at 18:09
  • what is the message on the exception you are getting? Is it something like ""\r\nExpected invocation on the mock once, but was 0 times: d => d.InsertInvoice(It.Is(i => (i.TermPaymentAmount = 0)))"" Or is it something else? – Mark Coleman Apr 15 '10 at 19:07
0

My guess is that it may have something to do with the problems inherent with equivalency checking of floating point values. Is your Invoice.TermPaymentAmount property value the result of a calculation, perhaps?

For an idea of what I'm talking about, see this possibly related question: Is it safe to check floating point values for equality to 0?

Community
  • 1
  • 1
Eric King
  • 11,594
  • 5
  • 43
  • 53
  • This is an excellent suggestion that a number of coworkers also recommended to me. Invoice.TermPaymentAmount is a decimal, and it is the result of a calculation, though in my test it is the result of adding a bunch of other zero values to each other, and when I actually run the test in my IDE and when I add the callback method to MessageBox.Show the value, it all appears to be just 0 without any minuscule decimal. I've tried testing whether the value is equal to 0, to 0m, have used Decimal.Equals, etc. but I can't get this test to pass. –  Apr 21 '10 at 14:54
  • I've also tried testing whether Math.Round(invoice.TermPaymentAmount,4) == 0, and that does not work. It feels as though the mocking library will insist that this test (specifically the mock.Verify() call) will fail no matter what. I'll definitely update this question when I figure out the answer! Thanks! –  Apr 21 '10 at 15:01