tags:

views:

115

answers:

3

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?

A: 

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...

Stephane
This was a good suggestion but in this case it didn't solve the problem. Thanks though!
Dave Falkner
A: 

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
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.
Dave Falkner
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<Invoice>(i => (i.TermPaymentAmount = 0)))"" Or is it something else?
Mark
A: 

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: http://stackoverflow.com/questions/485175/c-net-is-it-safe-to-check-floating-point-values-for-equality-to-0

Eric King
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.
Dave Falkner
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!
Dave Falkner