Як я можу метод, який має необов'язковий аргумент у своєму підписі, не чітко вказуючи його або використовуючи перевантаження?


119

З огляду на наступний інтерфейс:

public interface IFoo
{
    bool Foo(string a, bool b = false);
}

Спроба знущатися над ним за допомогою Moq:

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>())).Returns(false);

видає таку помилку під час компіляції:

Дерево виразів не може містити виклик або виклик, які використовують необов'язкові аргументи

Я виявив, що вищезазначена проблема піднята як розширення у списку питань Moq, і вона, як видається, призначена до версії 4.5 (коли це є).

Моє запитання: що мені робити з огляду на те, що вищезазначене не буде виправлено найближчим часом? Чи є мої параметри лише явно встановити значення за замовчуванням необов'язкового параметра кожного разу, коли я його знущаюсь (який тип перемагає точку вказування в першу чергу), або створити перевантаження без bool (як, що я зробив би до C # 4)?

Або хтось натрапив на більш розумний спосіб подолати це питання?


5
Чи було б розумно просто вказати It.IsAny <bool> () для другого параметра?
Пол д'Ауст

Через півтора року це все-таки вірно ..
Мукус

@Mukus, сміливо кидай PR, брато.
IamDOM

Відповіді:


91

Я вважаю, що ваш єдиний вибір зараз - це явно включити boolпараметр у налаштування для Foo.

Я не думаю, що це перешкоджає призначенню значення за замовчуванням. Значення за замовчуванням - це зручність для виклику коду, але я думаю, що ви повинні бути чіткими у своїх тестах. Скажіть, ви можете залишити, вказавши boolпараметр. Що станеться, якщо в майбутньому хтось змінить значення за замовчуванням bна true? Це призведе до провальних тестів (і це справедливо), але їх буде складніше виправити через приховане припущення, яке bє false. Явне зазначення boolпараметра має ще одну перевагу: воно покращує читабельність ваших тестів. Хтось, переходячи через них, швидко дізнається, що є одна Fooфункція, яка приймає два параметри. Це мої 2 копійки, принаймні :)

Що стосується того, щоб вказати його щоразу, коли ви з нього знущаєтесь, не дублюйте код: створюйте та / або ініціалізуйте макет у функції, щоб у вас залишилася лише одна точка зміни. Якщо ви дійсно цього хочете, ви можете подолати очевидний короткий результат Moq тут, дублюючи Fooпараметри в цій функції ініціалізації:

public void InitFooFuncOnFooMock(Mock<IFoo> fooMock, string a, bool b = false)
{
    if(!b)
    {
        fooMock.Setup(mock => mock.Foo(a, b)).Returns(false);
    }
    else
    {
        ...
    }
}

1
Відмінна відповідь; Я вже пішов і чітко вказав це в своїх глузуваннях, але ваша відповідь дуже чітко і логічно підтверджує, чому я повинен робити це саме так. Спасибі, @Chris.
Appulus

9
змінивши параметр за замовчуванням "повинен" порушити тести. Відмова від тестів, коли зміна за замовчуванням, може бути ознакою поганого тестування. Код може використовувати за замовчуванням, але тести ні?
Поп Каталін

Був певний час, але я спробував такий підхід з Moq, коли намагався знущатися над інтерфейсом (IDConnection в Dapper), і я все одно отримую ту ж помилку. Будь-які ідеї чому? Зразок рядка: mockDB.Setup (x => x.Query <MyObject> (It.IsAny <string> (), It.IsAny <DynamicParameters> (), It.IsAny <IDbTransaction> (), false, 600). Повернення (новий Список <MyObject> ()); Останні два значення - це необов'язкові параметри методу, який я встановлюю.
Raelshark

4
Arrgggh! Жахливий if (!x) {} else {}анти-узор :)
nicodemus13

1
@ nicodemus13 Так, але я намагався якомога більше наблизити приклад коду до прикладу ОП у запитанні. Я б не обов'язково виступав за це :)
Кріс Мантл,

8

Щойно зіткнувшись з цією проблемою, Moq не підтримує цей випадок використання. Отже, здається, що переосмислення методу було б достатнім для цього випадку.

public interface IFoo
{
    bool Foo(string a);

    bool Foo(string a, bool b);
}

Зараз доступні обидва методи, і цей приклад спрацює:

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>())).Returns(false);

2

Використовуючи Moq версії 4.10.1, я зміг зробити наступне

З інтерфейсом:

public interface IFoo
{
    bool Foo(string a, bool b = false);
}

І знущаються

var mock = new Mock<IFoo>();
mock.Setup(mock => mock.Foo(It.IsAny<string>(), It.IsAny<bool>())).Returns(false);

Вирішує виклик до Foo з першим параметром у порядку

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.