2009-04-07 7 views
18

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

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. Любые идеи о том, как протестировать это, будут оценены. Благодаря!

ответ

19

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

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

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

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

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

+0

Наверное, я не уверен, как бы вы это заглушили (хотя я мало что знаю о заглушках). Можете ли вы рассказать о том, как это можно было бы решить? –

+0

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

+0

У меня была подобная проблема, и я настолько обнулен, что работал над MoQ, я забыл, что могу написать свою собственную заглушку! Спасибо. –

9

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

«лямбда-выражение не может непосредственно захватить реф или параметр вне от метода вмещающей.»

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

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

Вы можете проверить дискуссии MOq для более http://groups.google.com/group/moqdisc

Удачи.

+0

Отличный ответ! :) – Delashmate

10

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

Взятые из QuickStart в 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); 
+6

Спасибо за ответ. Я видел это в быстром старте (на самом деле, я убедил своего босса обновиться до последней версии Moq, потому что я работал на нем, работая). К сожалению, поведение, похоже, не соответствует тому, что (очень неясно) предложено «Только совпадения, если аргумент ref для вызова является одним и тем же экземпляром». –

0

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

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

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

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

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

спасибо

 Смежные вопросы

  • Нет связанных вопросов^_^