Як знущатись над класом без інтерфейсу?


82

Я працюю над .NET 4.0 за допомогою C # у Windows 7.

Я хочу перевірити зв'язок між деякими методами, використовуючи макет. Єдина проблема полягає в тому, що я хочу це зробити, не впроваджуючи інтерфейс. Це можливо?

Я просто прочитав багато тем та кілька підручників про макетні об'єкти, але всі вони раніше використовували макет інтерфейсів, а не класів. Я намагався використовувати фреймворки Rhino та Moq.


1
Дійсно, що ці інструменти створені з точки зору використання виключно "IInterfaces".
AR

4
Вони створені припускаючи, що ви використовуєте інтерфейс на основі DI. Це досить стандартна схема в наші дні.
Месс

На жаль, цей шаблон суперечить "незмінному типу" шаблону :(
Matthew Watson

Відповіді:


67

Просто позначте будь-який спосіб, який вам потрібно підробляти, як virtual(а не приватний). Тоді ви зможете створити підробку, яка може замінити метод.

Якщо ви використовуєте new Mock<Type>і у вас немає конструктора без параметрів, тоді ви можете передати параметри як аргументи вищезазначеного виклику, оскільки він приймає типparam Objects


1
Це змушує мене задуматись, чи взагалі потрібно створювати інтерфейс для кожного класу, над яким я хочу знущатися. Хіба ми не могли б просто використовувати конкретні класи для глузування, якщо вони не мають інтерфейсу?
орад

14
@orad Насправді я схильний створювати клас спочатку, створюючи інтерфейс лише тоді, коли мені потрібно розбити загальну функціональність.
Justin Pihony

Як ви потім передаєте макет об'єкту, який очікує вказаний тип? Якщо я створюю фіктивний "новий фіктивний <MyType>" і намагаюся передати його об'єкту, який очікує MyType, я отримую помилку "Не вдається перетворити фіктивну <MyType> в MyType".
Нейтрино

2
Я розробив це, якщо ви визначаєте свій макет як 'var myMock = new Mock <MyType> ()', ви повинні передати його класу, який використовує макет як myMock.Object.
Нейтрино,

4
Основна проблема виникає, коли немає безпараметричного конструктора, а параметризований конструктор у закритому класі Concrete не є загальнодоступним. (Тут, маючи на увазі слово «закрито», я маю на увазі зовнішній пакет). Тоді не можна реалізувати інтерфейс, не можна зробити цей метод віртуальним, а також не можна використовувати параметризований конструктор.
Абхілаш Похріял

22

Більшість глузливих фреймворків (включаючи Moq та RhinoMocks) генерують проксі-класи як замінник вашого знущаного класу та замінюють віртуальні методи поведінкою, яку ви визначаєте. Через це ви можете знущатися над інтерфейсами або віртуальними методами на конкретних або абстрактних класах. Крім того, якщо ви знущаєтесь із конкретного класу, вам майже завжди потрібно надати безпараметричний конструктор, щоб знущальний фреймворк знав, як створити екземпляр класу.

Чому неприязнь до створення інтерфейсів у вашому коді?


107
Тому що він засмічує кодову базу тоннами інтерфейсів, які, якби не якесь технічне обмеження тестових платформ, були б абсолютно непотрібними?
BlueRaja - Danny Pflughoeft

9
Ви чули вираз "охоплення перевищує обсяг". Це пояснює, чому розробники завжди скаржаться. Мова C # не була розроблена з урахуванням модульного тестування. Ось чому насправді важко вводити фіктивні залежності без величезної метушні безглуздих інтерфейсів або віртуальних методів. Можливо, хтось незабаром винайде мову, яку легко перевірити модулем. На той час нам буде ще на що скаржитися.
Джон Хенкель,

13
Я радий знати, що я не єдиний, хто сприймає інтерфейси та віртуальні методи як безлад, якщо їх єдиною метою є тестування.
aaaaaa

13
"єдиною метою є тестування", але чи не важливим для вас є створення тестового коду?
MakkyNZ

13
"Чому неприязнь до створення інтерфейсів у вашому коді?" Тому що я не писав цей об’єкт і не маю доступу до коду. Мені потрібно створити макет закритого об'єкта, яким я не володію, який не реалізує інтерфейс.
Шон Уорл,

16

За допомогою MoQ ви можете висміяти конкретні класи:

var mocked = new Mock<MyConcreteClass>();

але це дозволяє замінити virtualкод (методи та властивості).


1
Я спробував це, але коли я запускаю свій тестовий проект, моя програма видає виняток: "Не вдається створити екземпляр проксі-класу" "Не вдалося знайти конструктор без параметрів."
Vinicius Seganfredo

2
Просто передайте параметри конструктора конструктору Mock <>. Напр.new Mock<MyConcreteClass>(param1, anotherParam, thirdParam, evenMoreParams);
Божидар Стойнефф,

1
Чому б просто не створити інтерфейс для класу?
Роберт Перрі

11

Я думаю, що краще створити інтерфейс для цього класу. І створити модульний тест за допомогою інтерфейсу.

Якщо у вас немає доступу до цього класу, ви можете створити адаптер для цього класу.

Наприклад:

public class RealClass
{
    int DoSomething(string input)
    {
        // real implementation here
    }
}

public interface IRealClassAdapter
{
    int DoSomething(string input);
}

public class RealClassAdapter : IRealClassAdapter
{
    readonly RealClass _realClass;

    public RealClassAdapter() => _realClass = new RealClass();

    int DoSomething(string input) => _realClass.DoSomething(input);
}

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

Сподіваюся, це працює.


4
Це страшно для обслуговування. Якщо ви хочете додати новий метод до RealClass, ви також повинні додати його до IReadClassAdapter, а також до RealClassAdapter. ПОТРІЙНІ зусилля! Краще рішення - просто додати "віртуальне" ключове слово до кожного загальнодоступного методу в RealClass.
Джон Хенкель,

12
@JohnHenckel Я припускаю, що ми не маємо доступу до RealClass. Отже, додавання "віртуального" методу, на мій погляд, не є можливим. Якщо у вас є доступ до реального класу, краще застосувати інтерфейс безпосередньо до нового класу, я думаю, що це найкраща практика.
Anang Satria

Дякую. Здається, це єдине законне рішення знущання над класами без доступу. Навіть це багато "дурних" кодів, мені це потрібно, щоб запустити код у "невідповідному середовищі"
Майкл Шпанбауер,

7

Стандартні фреймворки для насмішок створюють проксі-класи. Це причина, чому вони технічно обмежені інтерфейсами та віртуальними методами.

Якщо ви хочете знущатись і над "звичайними" методами, вам потрібен інструмент, який працює з інструментарієм замість генерації проксі. Наприклад, MS Moles і Typemock можуть це зробити. Але перший має жахливий "API", а другий є комерційним.


чи можемо ми висміяти звичайний клас цим загальнодоступним методом?
SivaRajini


2

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

Адаптер реалізує інтерфейс, тому макет може також реалізувати інтерфейс.

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


1

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

Наприклад:

Служба, яку ви хочете перевірити, яка не містить віртуальних методів або інтерфейсу реалізації

public class ServiceA{

public void A(){}

public String B(){}

}

Обгортка до moq

public class ServiceAWrapper : IServiceAWrapper{

public void A(){}

public String B(){}

}

Інтерфейс обгортки

public interface IServiceAWrapper{

void A();

String B();

}

У юніт-тесті тепер можна знущатись над обгорткою:

    public void A_Run_ChangeStateOfX()
    {
    var moq = new Mock<IServiceAWrapper>();
    moq.Setup(...);
    }

Це може бути не найкращою практикою, але якщо правила проекту змушують вас таким чином, зробіть це. Також помістіть усі свої обгортки всередину вашого проекту Unit Test або Helper, вказаного лише для модульних тестів, щоб не перевантажувати проект непотрібними обгортками або адаптерами.

Оновлення: Ця відповідь пішла більше року, але цього року я зіткнувся з багатьма подібними сценаріями з різними рішеннями. Наприклад, так просто використовувати Microsoft Fake Framework для створення макетів, підробок і заглушок і навіть тестування приватних та захищених методів без будь-яких інтерфейсів. Ви можете прочитати: https://docs.microsoft.com/en-us/visualstudio/test/isolating-code-under-test-with-microsoft-fakes?view=vs-2017

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