Проверьте значение параметра ссылки с Moq

Я просто переключился на Moq и столкнулся с проблемой. Я тестирую метод, который создает новый экземпляр бизнес-объекта, устанавливает свойства объекта из пользовательских значений ввода и вызывает метод (SaveCustomerContact) для сохранения нового объекта. Бизнес-объект передается как аргумент ref, поскольку он проходит через удаленный слой. Мне нужно проверить, что объект, передаваемый в SaveCustomerContact, имеет все свои свойства, установленные как ожидалось, но поскольку он создан как новый в методе контроллера, я не могу этого сделать.

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 );
}

Здесь тест:

[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();

}

Это лишь последняя из многих попыток получить этот параметр. Для справки значение фактического не изменяется от его начального (построенного) состояния.

Перемещение Assert.AreEqual(ожидается, актуально) после сбоя целевого вызова. Если я добавлю .Verifiable() к настройке, а не к .CallBack, а затем вызовет remote.Verify после цели (или, я полагаю, установить макет в строгу), он всегда терпит неудачу, потому что параметр, который я предоставляю в тесте, не является тот же экземпляр, что и тот, который создан в методе контроллера.

Я использую Moq 3.0.308.2. Любые идеи о том, как протестировать это, будут оценены. Спасибо!

Ответ 1

Я не могу предложить вам точное решение, но альтернативой было бы скрыть семантику pass-by-ref за адаптером, которая принимает параметр по значению и перенаправляет его в RemotingHandler. Это было бы проще высмеять и удалит "ref" бородавку из интерфейса (я всегда подозрительно отношусь к параметрам ref:-))

EDIT:

Или вы можете использовать заглушку вместо макета, например:

public class StubRemotingHandler : IRemotingHandler
{
    public CustomerContact savedContact;

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

Теперь вы можете проверить сохраненный объект в своем тесте:

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

Вы также скажете в своем комментарии:

Мне бы очень хотелось создать прецедент для обертывания случайных бит бэкэнд, чтобы я мог легче писать тесты

Я думаю, что именно тот прецедент, который вам нужно установить! Если ваш код не поддается тестированию, вы будете пытаться его протестировать. Упростите тестирование и увеличьте охват.

Ответ 2

Последняя версия Moq поддерживает этот сценарий.

Взято из быстрого запуска в 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);

Ответ 3

К сожалению, я не уверен, что это возможно без прямой поддержки Moq. Проблема в том, что выражения Lambda не поддерживают ref или out.

"Лямбда-выражение не может непосредственно фиксировать параметр ref или out из метода-оболочки".

http://msdn.microsoft.com/en-us/library/bb397687.aspx

Я даже не могу получить пример, подобный твоему. Добавление ref к установке не скомпилируется.

Возможно, вы захотите проверить обсуждения Moq для более http://groups.google.com/group/moqdisc

Удачи.

Ответ 4

Я столкнулся с подобной проблемой. Бит Я получил решение, используя последний Moq и передав значение, например

var instance = new Bar(); Mock.Setup(foo = > foo.Submit(ref instance)). Возвращает (true);

Раньше также я использовал тот же метод, но я не получал возврат как истинный.

Внутри фактического экземпляра функции создается и перезаписывается экземпляр, прошедший из класса unit test, вызывающий проблему. Я удалил создание экземпляра внутри фактического класса, а затем он сработал.

Надеюсь, это поможет вам.

спасибо