Використання moq для глузування лише над деякими методами


75

У мене є такий спосіб:

public CustomObect MyMethod()
{
    var lUser = GetCurrentUser();
    if (lUser.HaveAccess)
    {
        //One behavior
    }
    else 
    {
        //Other behavior
    }

    //return CustomObject
}

Я хочу глузувати IMyInterface.GetCurrentUser, щоб під час дзвінка MyMethodя міг дістатися до одного із шляхів коду, щоб перевірити його. Як це зробити за допомогою Moq?

Я роблю наступне:

var moq = new Mock<IMyInterface>();            
moq.Setup(x => x.GetCurrentUser()).Returns(lUnauthorizedUser);

//act
var lResult = moq.Object.MyMethod();

Але чомусь lResultзавжди, nullі коли я намагаюся потрапити MyMethodв налагодження, я завжди переходжу до наступного твердження.


1
Де ви lUnauthorizedUserініціалізували? Я гадаю, ви хотіли б щось на зразокmoq.Setup(x => x.GetCurrentUser()).Returns(new User() { HaveAccess = false });
Тайлер Ласощі

1
Тайлер, впевнений, що я m setting it in the above code, just didnвставив його, щоб короткий код був.
Ярослав Яковлєв

Відповіді:


157

Це називається частковим знущанням, і те, як я знаю, щоб це зробити в moq, вимагає знущання над класом, а не над інтерфейсом, а потім встановити для властивості "Callbase" для вашого знущаного об'єкта значення "true".

Для цього потрібно зробити усі методи та властивості класу, який ви тестуєте, віртуальним. Припускаючи, що це не проблема, ви можете написати тест на зразок цього:

 var mock = new Mock<YourTestClass>();
 mock.CallBase = true;
 mock.Setup(x => x.GetCurrentUser()).Returns(lUnauthorizedUser);
 mockedTest.Object.MyMethod();

1
Чудово! Велике спасибі, не так t think itпросто.
Ярослав Яковлєв

2
Я бачив подібні відповіді, але вони забули згадати, щоб встановити CallBaseістину, veeerrrry суттєву частину, дякую!
Mike de Klerk

1
Я чую його запах коду, щоб змінити код вашої програми лише для тесту. Що ви думаєте про цей сценарій у цьому світлі? У моєму випадку прямо зараз я просто тестую контролер ASP.Net REST, тому я не бачу жодної передбачуваної причини для підкласу контролера, але також не видається проблемою просто додати „віртуальний” до мої методи?
Еван

1
Тести модернізації @RuneStar - хитра річ - якщо ви модифікуєте код, щоб зробити його перевіряемым, тоді він не має тестів, тому ви не захищені від його регресії. позначати речі як віртуальні досить безпечно, оскільки ці речі йдуть. Якщо ви дійсно хотіли, ви можете обернути свій клас в інтерфейс (що в будь-якому випадку ви повинні робити в TDD), тоді налаштуйте виклики методів, які ви хочете глузувати, щоб викликати реальні методи. на мій погляд, надмірно, але в деяких ситуаціях варто мати у вікні інструментів (наприклад, ви просто хочете, щоб один виклик методу був справжньою реалізацією, а над іншими знущаються).
lee

36

Розширюючи відповідь Лі ,

Вам не потрібно робити усі методи та властивості вашого класу віртуальними, лише ті, з яких ви хочете знущатися.

Також слід зазначити, що ви повинні глузувати над конкретним виконанням вашого класу.

var mock = new Mock<YourTestClass>(); // vs. var mock = new Mock<IYourTestInterface>();

Якщо у вашому класі немає конструктора за замовчуванням, вам також потрібно буде вказати аргументи для передачі до нього через:

var mock = new Mock<YourTestClass>(x, y, z);
// or
var mock = new Mock<YourTestClass>(MockBehavior.Default, x, y, z);

Де x, y, zперший, другий та третій параметри для вашого конструктора відповідно.

І нарешті, якщо метод, який ви хочете знущатись, захищений, вам потрібно буде включити Moq.Protected

using Moq.Protected;

TReturnType returnValue = default(TReturnType);

mock.Protected()
    .Setup<TReturnType>("YourMockMethodName", It.IsAny<int>()) // methodname followed by arguments
    .Returns(returnValue);

6
Варто зазначити, що ви можете використовувати nameof(YourTestClass.YourMockMethodName)замість "YourMockMethodName". Завдяки цьому при рефакторингу метод імені тесту все одно працюватиме.
Shoter

32

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

var mock = new Mock<ITestClass>(); // Create Mock of interface

// Create instance of ITestClass implementation you want to use
var inst = new ActualTestClass();

// Setup to call method of an actual instance
// if method returns void use mock.Setup(...).Callback(...)
mock.Setup(m => m.SomeMethod(It.IsAny<int>())
    .Returns((int x) => inst.SomeMethod(x));

Тепер ви можете використовувати фактичний метод, а також використовувати речі, наприклад, Verifyщоб побачити, скільки разів його викликали.


1
Це справді цікава робота, ми можемо досягти того ж, не роблячи віртуальний метод класу.
Харшад Векарія,

Як щодо методів, які повертають null? Не буде виклику Повертань на макетному об’єкті? mock.Setup (m => m.SomeMethod (It.IsAny <int> ()). Повертає ((int x) => inst.SomeMethod (x));
Бендрам

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