Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Verify value of reference parameter with Moq

I just switched to Moq and have run into a problem. I'm testing a method that creates a new instance of a business object, sets the properties of the object from user input values and calls a method (SaveCustomerContact ) to save the new object. The business object is passed as a ref argument because it goes through a remoting layer. I need to test that the object being passed to SaveCustomerContact has all of its properties set as expected, but because it is instantiated as new in the controller method I can't seem to do so.

public void AddContact() {

    var contact = new CustomerContact() { CustomerId = m_model.CustomerId };

    contact.Name = m_model.CustomerContactName;
    contact.PhoneNumber = m_model.PhoneNumber;
    contact.FaxNumber = m_model.FaxNumber;
    contact.Email = m_model.Email;
    contact.ReceiveInvoiceFlag = m_model.ReceiveInvoiceFlag;
    contact.ReceiveStatementFlag = m_model.ReceiveStatementFlag;
    contact.ReceiveContractFlag = m_model.ReceiveContractFlag;
    contact.EmailFlag = m_model.EmailFlag;
    contact.FaxFlag = m_model.FaxFlag;
    contact.PostalMailFlag = m_model.PostalMailFlag;
    contact.CustomerLocationId = m_model.CustomerLocationId;

    RemotingHandler.SaveCustomerContact( ref contact );
}

Here's the test:

[TestMethod()]
public void AddContactTest() {

    int customerId = 0;

    string name = "a";

    var actual = new CustomerContact();

    var expected = new CustomerContact() {
        CustomerId = customerId,
        Name = name
    };

    model.Setup( m => m.CustomerId ).Returns( customerId );
    model.SetupProperty( m => model.CustomerContactName, name );
    model.SetupProperty( m => m.PhoneNumber, string.Empty );
    model.SetupProperty( m => m.FaxNumber, string.Empty );
    model.SetupProperty( m => m.Email, string.Empty );
    model.SetupProperty( m => m.ReceiveInvoiceFlag, false );
    model.SetupProperty( m => m.ReceiveStatementFlag, false );
    model.SetupProperty( m => m.ReceiveContractFlag, false );
    model.SetupProperty( m => m.EmailFlag, false );
    model.SetupProperty( m => m.FaxFlag, false );
    model.SetupProperty( m => m.PostalMailFlag, false );
    model.SetupProperty( m => m.CustomerLocationId, 0 );

    remote
        .Setup( r => r.SaveCustomerContact( ref actual ) )
        .Callback( () => Assert.AreEqual( actual, expected ) );

    target.AddContact();

}

This is just the most recent of many attempts to get ahold of that parameter. For reference, the value of actual does not change from its initial (constructed) state.

Moving the Assert.AreEqual(expected, actual) after the target call fails. If I add .Verifiable() to the setup instead of the .CallBack and then call remote.Verify after the target (or, I assume, set the mock to strict) it always fails because the parameter I provide in the test is not the same instance as the one that is created in the controller method.

I'm using Moq 3.0.308.2. Any ideas on how to test this would be appreciated. Thanks!

like image 353
Simon Calvin Avatar asked Apr 07 '09 16:04

Simon Calvin


People also ask

How can we use a Moq to check if a method is called with the correct parameters?

When you need to verify that the code under test called a method with the expected parameters, you can mock the method with Moq and use Verify() + It.Is<T>() to check the parameters passed in. Verify() asserts that the method call happened as expected with the specified parameters.

What does Moq verify do?

Verifies that all verifiable expectations have been met.

What can be mocked with Moq?

Using Moq, you can mock out dependencies and make sure that you are testing the code in isolation. Moq is a mock object framework for .

What is callback in Moq?

A callback is a piece of code that is passed into to a method parameter to be executed from within that method. Callbacks allow you to extend the functionality of such methods. When using Moq to create test doubles, you can supply callback code that is executed when an expectation is met.


2 Answers

I can't offer you an exact solution, but an alternative would be to hide the pass-by-ref semantics behind an adapter, which takes the parameter by value and forwards it to the RemotingHandler. This would be easier to mock, and would remove the "ref" wart from the interface (I am always suspicious of ref parameters :-) )

EDIT:

Or you could use a stub instead of a mock, for example:

public class StubRemotingHandler : IRemotingHandler
{
    public CustomerContact savedContact;

    public void SaveCustomerContact(ref CustomerContact contact)
    {
        savedContact = contact;
    }
}

You can now examine the saved object in your test:

IRemotingHandler remote = new StubRemotingHandler();
...
//pass the stub to your object-under-test
...
target.AddContact();
Assert.AreEqual(expected, remote.savedContact);

You also say in your comment:

I'd hate to start a precedent of wrapping random bits of the backend so I can write tests more easily

I think that's exactly the precedent you need to set! If your code isn't testable, you're going to keep struggling to test it. Make it easier to test, and increase your coverage.

like image 193
Jim Arnold Avatar answered Sep 19 '22 15:09

Jim Arnold


The latest version of Moq supports this scenario.

Taken from the quickstart at http://code.google.com/p/moq/wiki/QuickStart:

// ref arguments
var instance = new Bar();
// Only matches if the ref argument to the invocation is the same instance
mock.Setup(foo => foo.Submit(ref instance)).Returns(true);
like image 25
Praveen Angyan Avatar answered Sep 20 '22 15:09

Praveen Angyan