Какая хорошая стратегия?Тестирование с Mocks в C # без виртуального по умолчанию - PullRequest
2 голосов
/ 24 мая 2011

Я столкнулся с довольно большим недостатком C # в отношении насмешек и тестирования.И мои решения этой проблемы нежелательны.

У меня есть три класса, которые собираются вместе для выполнения некоторых функций.Не имеет никакого смысла использовать интерфейсы или явно объявлять какие-либо методы виртуальными, так как дизайн на самом деле не требует расширения или полиморфизма.Любая попытка сделать классы многократно используемыми только усложнит код.

Однако, поскольку я не объявил явно ни один из методов виртуальным, я не могу смоделировать классы и записать их вызовы с помощью фрейма.Псевдокод (с использованием Rhino.Mocks) будет выглядеть примерно так:

var b = mockRepo.StrickMock<ClassB>();
var c = mockRepo.StrickMock<ClassC>();
var classUnderTest = new ClassUnderTest{ B = b, C = c};

Expect.Call( b.MethodA );
Expect.Call( c.MethodB );

mockRepo.ReplayAll();
classUnderTest.DoSomething();
mockRepo.VerifyAll();

В его нынешнем виде мне придется сделать виртуальные b.MethodA и c.MethodB для этого.В качестве альтернативы я мог бы извлечь интерфейсы ClassB и ClassC и подделать их.Но, как я уже говорил, это только усложнит ситуацию.Я мог бы изменить дизайн классов, чтобы сделать его более пригодным для повторного использования, но это также усложнит ситуацию, и у функциональных возможностей очень мало шансов для повторного использования.

Как мне решить эту проблему и при этом стремиться поддерживать простоту междузависимости и исполняемый код?Я пропустил вариант?Какой подход вы бы предпочли выбрать?

Ответы [ 4 ]

2 голосов
/ 24 мая 2011

Интерфейсы - это шов для ваших юнит-тестов, чтобы легко подключать подделки.Я не понимаю, как они могут усложнить ситуацию (кроме увеличения количества типов).

Интерфейсы делают контракт между вызывающим и вызываемым явным.Также интерфейсы предоставляют возможности для создания «системы имен».Интерфейс также упрощает просмотр членов, которые не принадлежат (в отличие от классов, которые могут привлекать несвязанные методы).

1 голос
/ 24 мая 2011

Тестирование вашей функциональности менее чем идеально, убедившись, что ваш код вызывает процедуры своих зависимостей определенным образом.Ваш тест теперь привязан к реализации вашего метода вместо ожидаемого поведения.

Я бы рекомендовал начать с вопроса: какое поведение я пытаюсь проверить?Поведение - это набор предварительных условий, действие, которое вы хотите вызвать (т. Е. Вызов метода), и набор условий после публикации, которые вы хотите проверить.Например, если вы хотите проверить, что помещение элемента в стек увеличивает его счет на единицу, вы бы написали тест с именем:

public void Push_OnAnyStack_IncreasesCountByOne()

В целом, вы можете назвать свои тесты следующим шаблоном:

public void MethodToCall_WithGivenStateAndInputs_PerformsExpectedResult

Я согласен с вами, что введение интерфейсов ради интерфейсов может быть слишком трудоемким.Поэтому вы должны взглянуть на структуру своего кода и посмотреть, есть ли способ проверить поведение, которое вы хотите протестировать, установив предварительные условия (организовав свой тест), действуя путем вызова определенного метода и утверждения на постусловиях, запрашивая состояниеобъект, который вы тестируете.Таким образом, вы меньше заботитесь о том, как реализован ваш метод, и больше беспокоитесь о его поведении.Если вы обнаружите, что это невозможно, это свидетельствует о том, что вам необходимо провести реструктуризацию вашего проекта, чтобы прояснить обязанности.Это также может указывать на то, что интерфейсы на самом деле являются правильным решением.

0 голосов
/ 25 мая 2011

Одной из повторяющихся тем в принципах SOLID является то, что вы должны зависеть от абстракций (принцип открытого закрытого типа / принцип инверсии зависимостей).Независимо от того, используете ли вы интерфейсы или абстрактные классы с виртуальными методами, насмешка заключается в тестировании отношений или контракта между объектами.

Если вы используете фальшивый фреймворк, эти инструменты используют динамический прокси для динамической генерации реализации для проверки на основе поведения.Эта стратегия подразумевает, что вы используете абстракции, которые можно заменить.

Другие фреймворки, такие как TypeMock, используют Profiler API для перехвата и перезаписи IL перед его компиляцией JIT.Это платный продукт и немного медленный, но он позволяет вам перехватывать что угодно.

Лично, учитывая варианты.Я бы взял бесплатный инструмент и дизайн в сторону абстракций.Код будет менее гибким, но в целом более предсказуемым и более простым для тестирования.

0 голосов
/ 24 мая 2011

Я бы определенно выбрал интерфейсы.

...