Знущання над об'єктами за допомогою Moq, коли конструктор має параметри


94

У мене є об’єкт, над яким я намагаюся знущатися, використовуючи moq. Конструктор об'єкта має необхідні параметри:

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

Зараз я намагаюся створити макет для цього об'єкта, використовуючи або синтаксис v3 "налаштування" moq, або синтаксис 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);

Чи можете ви навести зразок методу, який ви хочете підтвердити, що його викликають?
Ciaran

4
Отже, якщо у мене є залежності від класів, а не інтерфейсів, я повинен знущатися навіть над їх залежностями, це знижується рекурсивно. Зрештою, я змушений використовувати деякі інтерфейси, щоб мій код перевірявся, навіть якщо мені не потрібні інтерфейси в моєму коді. Я думаю, що занадто багато інтерфейсів є більшим запахом, ніж глузування з конкретних класів ...
Таріон

Відповіді:


34

Останній рядок дає вам реальний екземпляр, оскільки ви використовуєте нове ключове слово, а не глузуєте з CustomerSyncEngine.

Ви повинні використовувати Mock.Of<CustomerSyncEngine>()

Єдина проблема з типами знущань над конкретним полягає в тому, що Moq потрібен публічний конструктор за замовчуванням (без параметрів) АБО вам потрібно створити Moq із специфікацією аргументу конструктора. http://www.mockobjects.com/2007/04/test-smell-mocking-concrete-classes.html

Найкраще клацнути правою кнопкою миші на своєму класі та вибрати інтерфейс Extract.


3
Що стосується проблеми, альтернативою є використання контейнера AutoMocking. Моїм улюбленим є Machine.Fakes спільно з Machine.Specification, використовуючи автомобільний контейнер, полегшує тестування менших площ поверхні. Припустимо, що Ендрю потрібно було протестувати метод у CustomerSyncEngineтому, що ICrmProviderдля всіх 3-х інтерфейсів повинні бути передбачені лише використання із традиційними реалізаціями насмішок, тоді як контейнер автоматичного змикування дозволить вам надати лише один.
Chris Marisic

74

Змініть останній рядок на

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

і це повинно спрацювати


3
Не знаєте, як цей коментар стосується моєї відповіді?
Сухас

2
Оскільки це призведе до помилки компіляції, оскільки mockLogger та інші видадуть виняток, що вони не мають властивості Object
Джастін Піхоні,

2
Оскільки OP використовує Mock.Of <T> () для створення макетів типів реєстратора, crm та кешу, повернутий об’єкт повертається як T, а не як Mock <T>. Отже, mockLogger.Object тощо не потрібен при передачі їх у Макет CustomerSyncEngine і, як згадував @JustinPihony, повинен показати вам помилку під час проектування.
Джош Густ,

1
@suhas Не повинно бути йогоnew Mock<CustomerSyncEngine>(new object[]{mockLogger, mockCrm, mockCache}).Object;
GiriB

@GiriB не потрібен, але можливий, оскільки макет визначається за допомогою Params. public Mock (params object [] args)
Jiří Herník
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.