Мокинг объектов с помощью Moq, когда у конструктора есть параметры

У меня есть объект, который я пытаюсь высмеять с помощью moq. У конструктора объекта есть обязательные параметры:

public class CustomerSyncEngine {
    public CustomerSyncEngine(ILoggingProvider loggingProvider, 
                              ICrmProvider crmProvider, 
                              ICacheProvider cacheProvider) { ... }
}

Теперь я пытаюсь создать макет для этого объекта, используя синтаксис moq v3 «setup» или v4 «Mock.Of», но не могу понять этого ... все, что я пытаюсь, не проверяется. Вот что у меня есть, но последняя строка дает мне реальный объект, а не макет. Причина, по которой я это делаю, заключается в том, что у меня есть методы в CustomerSyncEngine, которые я хочу проверить, вызываются ...

// setup
var mockCrm = Mock.Of<ICrmProvider>(x => x.GetPickLists() == crmPickLists);
var mockCache = Mock.Of<ICacheProvider>(x => x.GetPickLists() == cachePickLists);
var mockLogger = Mock.Of<ILoggingProvider>();

// need to mock the following, not create a real class like this...
var syncEngine = new CustomerSyncEngine(mockLogger, mockCrm, mockCache);

moq
person Andrew Connell    schedule 14.09.2011    source источник
comment
Можете ли вы предоставить образец метода, который вы хотите проверить, когда вас вызывают?   -  person Ciaran    schedule 12.06.2012
comment
Поэтому, если у меня есть зависимости от классов, а не от интерфейсов, я должен высмеивать даже их зависимости, это происходит рекурсивно. В конце концов, я вынужден использовать некоторые интерфейсы, чтобы мой код оставался тестируемым, даже если мне не нужны интерфейсы в моем коде. Я думаю, что слишком много интерфейсов - это больший запах, чем издевательство над конкретными классами ...   -  person Tarion    schedule 23.02.2013


Ответы (2)


Последняя строка дает вам реальный экземпляр, потому что вы используете ключевое слово new, а не имитируете CustomerSyncEngine.

Вы должны использовать Mock.Of<CustomerSyncEngine>()

Единственная проблема с типами Mocking Concrete заключается в том, что Moq потребуется общедоступный конструктор по умолчанию (без параметров) ИЛИ вам нужно создать Moq со спецификацией конструктора arg. http://www.mockobjects.com/2007/04/test-smell-mocking-concrete-classes.html

Лучше всего щелкнуть правой кнопкой мыши свой класс и выбрать «Извлечь интерфейс».

person Raghu    schedule 18.09.2011
comment
Что касается проблемы, альтернативой является использование контейнера AutoMocking. Мне больше всего нравится Machine.Fakes в сочетании с Machine.Specifications, использование автомобильного контейнера упрощает тестирование небольших площадей. Предположим, Эндрю нужно было протестировать метод в CustomerSyncEngine, который использует только ICrmProvider с традиционными реализациями имитации, должен быть предоставлен для всех 3 интерфейсов, тогда как контейнер autmocking позволит вам предоставить только один. - person Chris Marisic; 14.10.2014

Измените последнюю строку на

var syncEngine = new Mock<CustomerSyncEngine>(mockLogger, mockCrm, mockCache).Object;

и это должно работать

person Suhas    schedule 09.07.2012
comment
Не знаете, как этот комментарий относится к моему ответу? - person Suhas; 02.05.2013
comment
Потому что это вызовет ошибку компиляции, поскольку mockLogger и другие вызовут исключение, что у них нет свойства Object. - person Justin Pihony; 02.05.2013
comment
Поскольку OP использует Mock.Of ‹T› () для создания имитов типов регистратора, crm и кеша, возвращаемый объект возвращается как T, а не как Mock ‹T›. Таким образом, mockLogger.Object и т. Д. Не требуется при передаче их в Mock of CustomerSyncEngine и, как упоминалось в @JustinPihony, должен показать вам ошибку времени разработки. - person Josh Gust; 08.02.2018
comment
@suhas Не должно быть его new Mock<CustomerSyncEngine>(new object[]{mockLogger, mockCrm, mockCache}).Object; - person GiriB; 29.03.2019
comment
@GiriB не требуется, но возможно, поскольку макет определяется с помощью Params. общедоступный макет (объект params [] args) - person Jiří Herník; 22.01.2020